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=$!Updated 10 months ago