Skip to main content

Jobs Around the Engine

The engine runs one loop per camera. But a fleet needs work that doesn't belong in any single camera's loop: checking that cameras are reachable, shipping captured images to long-term archive, trimming old database rows, answering "which cameras are up right now." Those live in Alert.CA.CameraOperation, a Functions app that is essentially the fleet's operational side tasks.

The one idea to hold on to​

CameraOperation is a collection of independent, trigger-driven jobs — timers, queues, and a durable health-check orchestration — that surround the engine without being part of its per-camera loop. Each job has one responsibility and its own trigger:

JobTriggerWhat it does
CameraPingOrchestratorDurable (HTTP-started)Pings every camera for its model number, writes unreachable ones to blob
PublishBlobFunctionQueue (cache-*)Forwards cached images to the external archive service
SqlDataPruningFunctionTimer (nightly 11:00)Deletes aged rows from audit/lease/movement/COCO/Condor tables
GetRunningOnlineCamerasHTTPReports which cameras are running and online
UcsdCameraConfigRefresherTimer (5 min)Inventory sync — see Inventory & Bootstrap

Health checks are their own durable orchestration​

Pinging the whole fleet is itself a fan-out job, so it uses Durable Functions — but a transient orchestration, not the engine's eternal one. CameraPingOrchestrator gets the device list (GetCameraIdRequest), asks each camera for its model number in parallel (GetModelNumberByDevice, through the VAPIX client), and writes the ones that didn't answer to blob storage (WriteErrorCamerasToBlob). It's started on demand via an HTTP trigger and runs to completion — the opposite lifecycle from the acquisition loop.

Forwarding images to the archive​

When the engine caches an image it drops a message on a cache-single-image, cache-pano-image, or cache-prestitch-zip-archive queue. PublishBlobFunction is the consumer: each queue trigger picks up the payload and POSTs it to the external archive service (with an API key). This is the downstream half of the engine's image pipeline — the producer is the engine, the consumer is here.

Gotcha: several of these are compiled out in DEBUG

The archive-forwarding and the UCSD refresher (and other prod jobs) are wrapped in #if !DEBUG, so they don't run from a developer machine against real infrastructure. If you're running CameraOperation locally and a queue isn't draining or inventory isn't syncing, that's expected — those jobs only exist in a non-DEBUG build.

Wrapping up​

CameraOperation is the fleet's operational toolbox: a transient durable orchestration for health pings, queue consumers that forward cached images to the archive, a nightly SQL pruner, and an HTTP status endpoint that reads the monitoring mirror rather than the engine. Each is an independent trigger-driven job, deliberately separate from the per-camera acquisition loop.

One more integration lives here — pulling emergency incidents into the map: PulsePoint Incidents.