Running Update Service

Throughput both the OpenCV Luxonis tutorial and the ROS tutorial, we use the trigger_file_path as an assumed value, and mention the Update Service as the method for keeping the triggers in that path updated. Let's dig into that a little bit.

To recap, the role of the on-device update service is to keep trigger definitions up-to-date. This implementation is left up to the customer, given the varying network topologies and authorization schemes of their respective edge devices. Here, we provide a simple example of how one might go about it.

There are a couple of dependencies:

pip3 install APScheduler requests

Basically, this implementation of the update-service takes in one argument: the name of the device. This is something the user can decide, but it's critical that the name of the device is consistent with the name of the device registered in the Smart Capture Web Interface. This allows for the appropriate triggers to be downloaded.

Once run, this script will save the trigger definitions to a file that looks like ${DEVICE_NAME}.json every 10 seconds. It will also upload the logs written to the Smart Capture Client's sc_log.log to the web interface.

In order for the preceding tutorials to operate off of the most up-to-date triggers, the trigger filename written to here, and loaded from in the SDK, must be the same.

The full update service code:

from apscheduler.schedulers.background import BlockingScheduler
import argparse
import base64
import pickle
import json
import requests
from demo import config

api_key = config.API_KEY
encoded_key = base64.b64encode((api_key + ":").encode()).decode()
SMARTCAPTURE_SCALE_ENDPOINT = "https://api.scale.com/v1/smart_capture"


def get_device_id(name):
    resp = requests.get(
        f"{SMARTCAPTURE_SCALE_ENDPOINT}/all_devices",
        headers={"Authorization": "Basic " + encoded_key},
    )
    for device in resp.json():
        if device["name"] == name:
            return device["id"]
    return None


def save_triggers(device, smartcapture_endpoint):
    url = f"{smartcapture_endpoint}/device/{device}/triggers"
    resp = requests.get(
        url,
        headers={"Authorization": "Basic " + encoded_key},
    )
    if resp.status_code != 200:
        print("Could not get triggers for device")
        return

    resp = resp.json()

    trigger_data = {}
    if "autotags" in resp:
        autotags = []
        for autotag in resp["autotags"]:
            model = pickle.loads(bytes(autotag["svm"]["data"]))
            autotag[autotag["id"]] = model.coef_[0].tolist()
            autotags.append(autotag)
        trigger_data["autotags"] = autotags

    trigger_data["triggers"] = resp["triggers"]

    json.dump(trigger_data, open(device + ".json", "w"))
    print("Saving triggers for device: " + device)


def upload_logs(device, smartcapture_endpoint):
    data = []
    with open("sc_log.log") as f:
        data = [json.loads(line) for line in f.readlines() if line.strip()]

    payload = {"logs": data}

    url = f"{smartcapture_endpoint}/device/{device}/logs"
    resp = requests.post(
        url,
        json=payload,
        headers={"Authorization": "Basic " + encoded_key},
    )

    if resp.status_code != 200:
        print("Could not upload logs for device")
        return

    # Clear the log file
    open("sc_log.log", "w").close()


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Smart Capture Update Service")
    parser.add_argument("device_name", type=str)

    args = parser.parse_args()

    device = args.device_name
    device = get_device_id(device)
    
    if device:
        sched = BlockingScheduler()
        sched.add_job(
            save_triggers,
            "interval",
            seconds=3,
            args=[device, SMARTCAPTURE_SCALE_ENDPOINT],
        )
        sched.add_job(
            upload_logs,
            "interval",
            seconds=10,
            args=[device, SMARTCAPTURE_SCALE_ENDPOINT],
        )
        sched.start()

The above can be run inside of a bash script or other tool in this way:

trap 'kill $BGPID; exit' INT SIGINT SIGTERM EXIT
python3 update_service.py DEVICE_NAME &
BGPID=$!

Did this page help you?