@camstack/addon-pipeline 1.2.75 → 1.2.77
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{addon-utils-a_36HH_N.js → addon-utils-bZcwLaVW.js} +1 -1
- package/dist/audio-analyzer/index.js +2 -2
- package/dist/audio-analyzer/index.mjs +1 -1
- package/dist/detection-pipeline/index.js +4 -4
- package/dist/detection-pipeline/index.mjs +2 -2
- package/dist/{dist-6ZJCV0jY.js → dist-DQZzL0Ri.js} +452 -42
- package/dist/{dist-T_8jMw7d.mjs → dist-HhcoV-Ky.mjs} +452 -42
- package/dist/{event-loop-stall-monitor-Cjof8vA1.js → event-loop-stall-monitor-CBOo1oh_.js} +1 -1
- package/dist/{event-loop-stall-monitor-XeGmL4xr.mjs → event-loop-stall-monitor-CqBEWGSZ.mjs} +1 -1
- package/dist/{lazy-sharp-DvwPafeP.js → lazy-sharp-DIC1rpea.js} +1 -1
- package/dist/motion-wasm/index.js +2 -2
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/pipeline-runner/index.js +278 -15
- package/dist/pipeline-runner/index.mjs +277 -14
- package/dist/recorder/index.js +368 -18
- package/dist/recorder/index.mjs +367 -17
- package/dist/session-decode/decode-worker-child.js +2 -2
- package/dist/session-decode/decode-worker-child.mjs +1 -1
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-H2OTmgR6.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-De_xzvjU.mjs} +3 -3
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DHJSxHH1.mjs +26 -0
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-Dg08SxUW.mjs +26 -0
- package/dist/stream-broker/{hostInit-4XDN_Nt0.mjs → hostInit-CDS5frmk.mjs} +3 -3
- package/dist/stream-broker/index.js +306 -71
- package/dist/stream-broker/index.mjs +306 -71
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/dist/{worker-protocol-SqED7Y9i.mjs → worker-protocol-BNlLGLBV.mjs} +1 -1
- package/dist/{worker-protocol-B0cbTXyw.js → worker-protocol-DZVGVAPU.js} +1 -1
- package/package.json +1 -1
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-RYTOS6B-.mjs +0 -26
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DfsLsn2e.mjs +0 -26
package/dist/{event-loop-stall-monitor-XeGmL4xr.mjs → event-loop-stall-monitor-CqBEWGSZ.mjs}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as COCO_TO_MACRO, i as COCO_80_LABELS, r as AUDIO_MACRO_LABELS, z as hfModelUrl } from "./dist-
|
|
1
|
+
import { a as COCO_TO_MACRO, i as COCO_80_LABELS, r as AUDIO_MACRO_LABELS, z as hfModelUrl } from "./dist-HhcoV-Ky.mjs";
|
|
2
2
|
import { PerformanceObserver } from "node:perf_hooks";
|
|
3
3
|
//#region src/detection-pipeline/registry/model-catalogs.ts
|
|
4
4
|
var HF_REPO = "camstack/camstack-models";
|
|
@@ -2,8 +2,8 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
6
|
-
const require_lazy_sharp = require("../lazy-sharp-
|
|
5
|
+
const require_dist = require("../dist-DQZzL0Ri.js");
|
|
6
|
+
const require_lazy_sharp = require("../lazy-sharp-DIC1rpea.js");
|
|
7
7
|
let node_path = require("node:path");
|
|
8
8
|
let node_fs = require("node:fs");
|
|
9
9
|
//#region src/motion-wasm/wasm-motion-detector.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { H as motionDetectionCapability, R as evaluateZoneRules, ft as hydrateSchema, st as BaseAddon, ut as DeviceType } from "../dist-
|
|
1
|
+
import { H as motionDetectionCapability, R as evaluateZoneRules, ft as hydrateSchema, st as BaseAddon, ut as DeviceType } from "../dist-HhcoV-Ky.mjs";
|
|
2
2
|
import { t as getSharp } from "../lazy-sharp-6oymT_yf.mjs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { readFileSync } from "node:fs";
|
|
@@ -2,11 +2,11 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
6
|
-
const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-
|
|
7
|
-
const require_worker_protocol = require("../worker-protocol-
|
|
5
|
+
const require_dist = require("../dist-DQZzL0Ri.js");
|
|
6
|
+
const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-CBOo1oh_.js");
|
|
7
|
+
const require_worker_protocol = require("../worker-protocol-DZVGVAPU.js");
|
|
8
8
|
const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
|
|
9
|
-
const require_lazy_sharp = require("../lazy-sharp-
|
|
9
|
+
const require_lazy_sharp = require("../lazy-sharp-DIC1rpea.js");
|
|
10
10
|
const require_remote_restream = require("../remote-restream-BYbAsgUf.js");
|
|
11
11
|
let sharp = require("sharp");
|
|
12
12
|
sharp = require_dist.__toESM(sharp);
|
|
@@ -2366,13 +2366,36 @@ function summarizeRunnerHardware(hardware, cpuCores = node_os.cpus().length) {
|
|
|
2366
2366
|
* just picked one up — which coincide with motion in the package zone
|
|
2367
2367
|
* SETTLING (transitioning from moving to still). This governor fires:
|
|
2368
2368
|
*
|
|
2369
|
-
* - on a **settle edge**: motion was observed, then has
|
|
2370
|
-
* `settleMs` (kills flicker between motion frames and the
|
|
2371
|
-
* set down then snatched back" case)
|
|
2369
|
+
* - on a **settle edge**: motion NEAR THE PACKAGE ZONE was observed, then has
|
|
2370
|
+
* been absent for `settleMs` (kills flicker between motion frames and the
|
|
2371
|
+
* "bag briefly set down then snatched back" case). The settle edge opens a
|
|
2372
|
+
* **BURST**, and
|
|
2372
2373
|
* - on a **low-cadence safety poll** (`pollIntervalMs`, ~30–60s): a
|
|
2373
2374
|
* backstop so a delivery whose motion was never observed (a session-gap
|
|
2374
2375
|
* camera that only decodes intermittently) is still eventually caught.
|
|
2375
2376
|
*
|
|
2377
|
+
* ## The burst, and why the poll alone was not enough
|
|
2378
|
+
*
|
|
2379
|
+
* A parcel appears at the END of a motion episode — the courier walks up, puts
|
|
2380
|
+
* it down, walks away. That is a settle edge, and it is the moment we know
|
|
2381
|
+
* something MIGHT have been left behind. So the settle edge does not fire one
|
|
2382
|
+
* inference, it opens a burst: `burstCount` runs `burstIntervalMs` apart.
|
|
2383
|
+
*
|
|
2384
|
+
* The poll alone made the downstream promotion criterion unreachable in
|
|
2385
|
+
* PRACTICE and the notification useless in LATENCY. One observation per 45s
|
|
2386
|
+
* (and on a session-dispatch camera the measured gap was ~2 minutes) meant a
|
|
2387
|
+
* parcel needed minutes to accumulate enough evidence to be called parked, if
|
|
2388
|
+
* it ever did. The burst supplies the whole evidence chain inside ~20s, so a
|
|
2389
|
+
* delivery is reported while the courier is still in the street.
|
|
2390
|
+
*
|
|
2391
|
+
* The poll stays, unchanged in role: it is the SAFETY NET for the parcel whose
|
|
2392
|
+
* motion was never observed at all — a camera that just came up, an occlusion,
|
|
2393
|
+
* a missed analyzer frame. It is not the primary path any more.
|
|
2394
|
+
*
|
|
2395
|
+
* The two feed the SAME downstream round counter (`PackageStillnessTracker`),
|
|
2396
|
+
* which is what lets one promotion criterion serve both cadences. That is the
|
|
2397
|
+
* relation `scripts/check-package-promotion-satisfiable.ts` enforces.
|
|
2398
|
+
*
|
|
2376
2399
|
* Pure logic with an injected clock (every method takes `now`), mirroring
|
|
2377
2400
|
* `FrameGovernor` / `recomputeGovernor(now)` — no timers, no `Date.now()`
|
|
2378
2401
|
* inside, fully unit-testable. Per-device state lives in a Map; the caller
|
|
@@ -2391,14 +2414,37 @@ function summarizeRunnerHardware(hardware, cpuCores = node_os.cpus().length) {
|
|
|
2391
2414
|
*/
|
|
2392
2415
|
/** The step id (`PipelineStepInput.addonId`) the governor gates. */
|
|
2393
2416
|
var PACKAGE_DETECTION_STEP_ID = "package-detection";
|
|
2417
|
+
/**
|
|
2418
|
+
* Burst: 5 runs, 5s apart — 0s, 5s, 10s, 15s, 20s after the settle edge.
|
|
2419
|
+
*
|
|
2420
|
+
* Sized against the two downstream numbers it has to satisfy, both pinned by
|
|
2421
|
+
* `scripts/check-package-promotion-satisfiable.ts`:
|
|
2422
|
+
*
|
|
2423
|
+
* - `PACKAGE_STILLNESS_ROUND_GAP_MS` (5 000) — the spacing must be ≥ this or
|
|
2424
|
+
* the runs collapse into one round;
|
|
2425
|
+
* - `packageDropDwellSec` (15s default) — the burst SPAN
|
|
2426
|
+
* (`(burstCount - 1) × burstIntervalMs` = 20s) must clear the dwell, or the
|
|
2427
|
+
* burst ends before the parcel has been still long enough to count and the
|
|
2428
|
+
* delivery falls back to the poll. 20s over 15s is one whole run of margin,
|
|
2429
|
+
* which is what absorbs a dropped analyzer frame.
|
|
2430
|
+
*
|
|
2431
|
+
* Cost: 5 extra inferences per motion episode, on cameras with an active
|
|
2432
|
+
* package zone only (the caller consults this governor only while the
|
|
2433
|
+
* `package-detection` step is in the camera's tree, and the resolver appends
|
|
2434
|
+
* that step only for an enabled `package`-stage zone rule).
|
|
2435
|
+
*/
|
|
2394
2436
|
var DEFAULT_MOTION_SETTLE_CONFIG = {
|
|
2395
2437
|
settleMs: 3e3,
|
|
2396
|
-
pollIntervalMs: 45e3
|
|
2438
|
+
pollIntervalMs: 45e3,
|
|
2439
|
+
burstCount: 5,
|
|
2440
|
+
burstIntervalMs: 5e3
|
|
2397
2441
|
};
|
|
2398
2442
|
var INITIAL_STATE = {
|
|
2399
2443
|
lastMotionAt: null,
|
|
2400
2444
|
armed: false,
|
|
2401
|
-
lastRunAt: Number.NEGATIVE_INFINITY
|
|
2445
|
+
lastRunAt: Number.NEGATIVE_INFINITY,
|
|
2446
|
+
burstRemaining: 0,
|
|
2447
|
+
burstNextAt: 0
|
|
2402
2448
|
};
|
|
2403
2449
|
var NO_RUN = {
|
|
2404
2450
|
run: false,
|
|
@@ -2424,18 +2470,35 @@ var MotionSettleGovernor = class {
|
|
|
2424
2470
|
this.state.set(sample.deviceId, {
|
|
2425
2471
|
lastMotionAt,
|
|
2426
2472
|
armed: false,
|
|
2427
|
-
lastRunAt: sample.now
|
|
2473
|
+
lastRunAt: sample.now,
|
|
2474
|
+
burstRemaining: Math.max(0, this.config.burstCount - 1),
|
|
2475
|
+
burstNextAt: sample.now + this.config.burstIntervalMs
|
|
2428
2476
|
});
|
|
2429
2477
|
return {
|
|
2430
2478
|
run: true,
|
|
2431
2479
|
reason: "settle"
|
|
2432
2480
|
};
|
|
2433
2481
|
}
|
|
2482
|
+
if (prev.burstRemaining > 0 && sample.now >= prev.burstNextAt) {
|
|
2483
|
+
this.state.set(sample.deviceId, {
|
|
2484
|
+
lastMotionAt,
|
|
2485
|
+
armed,
|
|
2486
|
+
lastRunAt: sample.now,
|
|
2487
|
+
burstRemaining: prev.burstRemaining - 1,
|
|
2488
|
+
burstNextAt: sample.now + this.config.burstIntervalMs
|
|
2489
|
+
});
|
|
2490
|
+
return {
|
|
2491
|
+
run: true,
|
|
2492
|
+
reason: "burst"
|
|
2493
|
+
};
|
|
2494
|
+
}
|
|
2434
2495
|
if (pollReady) {
|
|
2435
2496
|
this.state.set(sample.deviceId, {
|
|
2436
2497
|
lastMotionAt,
|
|
2437
2498
|
armed,
|
|
2438
|
-
lastRunAt: sample.now
|
|
2499
|
+
lastRunAt: sample.now,
|
|
2500
|
+
burstRemaining: prev.burstRemaining,
|
|
2501
|
+
burstNextAt: prev.burstNextAt
|
|
2439
2502
|
});
|
|
2440
2503
|
return {
|
|
2441
2504
|
run: true,
|
|
@@ -2445,7 +2508,9 @@ var MotionSettleGovernor = class {
|
|
|
2445
2508
|
this.state.set(sample.deviceId, {
|
|
2446
2509
|
lastMotionAt,
|
|
2447
2510
|
armed,
|
|
2448
|
-
lastRunAt: prev.lastRunAt
|
|
2511
|
+
lastRunAt: prev.lastRunAt,
|
|
2512
|
+
burstRemaining: prev.burstRemaining,
|
|
2513
|
+
burstNextAt: prev.burstNextAt
|
|
2449
2514
|
});
|
|
2450
2515
|
return NO_RUN;
|
|
2451
2516
|
}
|
|
@@ -2459,6 +2524,90 @@ var MotionSettleGovernor = class {
|
|
|
2459
2524
|
}
|
|
2460
2525
|
};
|
|
2461
2526
|
//#endregion
|
|
2527
|
+
//#region src/pipeline-runner/package-zone-proximity.ts
|
|
2528
|
+
/**
|
|
2529
|
+
* How much the zone-union box grows on EVERY side, as a fraction of its own
|
|
2530
|
+
* width / height.
|
|
2531
|
+
*
|
|
2532
|
+
* 0.5 means a doormat 271px wide on a 640px frame is approached through a
|
|
2533
|
+
* ~135px apron on each side — about one adult's width at that distance, which
|
|
2534
|
+
* is the thing being detected: a person standing next to the mat, not on it.
|
|
2535
|
+
* Larger and a passer-by on the pavement opens a burst; smaller and the
|
|
2536
|
+
* courier who never steps on the mat does not.
|
|
2537
|
+
*/
|
|
2538
|
+
var PACKAGE_ZONE_DILATION = .5;
|
|
2539
|
+
/** Grow a box by {@link PACKAGE_ZONE_DILATION}, clamped to the frame. */
|
|
2540
|
+
function dilateZoneBox(box, dilation = PACKAGE_ZONE_DILATION) {
|
|
2541
|
+
const dx = box.w * dilation;
|
|
2542
|
+
const dy = box.h * dilation;
|
|
2543
|
+
const x = Math.max(0, box.x - dx);
|
|
2544
|
+
const y = Math.max(0, box.y - dy);
|
|
2545
|
+
return {
|
|
2546
|
+
x,
|
|
2547
|
+
y,
|
|
2548
|
+
w: Math.min(1, box.x + box.w + dx) - x,
|
|
2549
|
+
h: Math.min(1, box.y + box.h + dy) - y
|
|
2550
|
+
};
|
|
2551
|
+
}
|
|
2552
|
+
function intersects(a, b) {
|
|
2553
|
+
return a.x < b.x + b.w && b.x < a.x + a.w && a.y < b.y + b.h && b.y < a.y + a.h;
|
|
2554
|
+
}
|
|
2555
|
+
/**
|
|
2556
|
+
* Does any motion region reach the dilated package zone?
|
|
2557
|
+
*
|
|
2558
|
+
* **Fail-open on unknown geometry.** `zoneUnion === undefined` means the caller
|
|
2559
|
+
* has not (or could not) resolve the zones — a cold cache, a failed cap read.
|
|
2560
|
+
* It answers "yes, if there is any motion at all", which degrades to exactly
|
|
2561
|
+
* the whole-frame behaviour this replaced. The alternative — answering "no" —
|
|
2562
|
+
* would let one failed read silence package detection on a camera for as long
|
|
2563
|
+
* as the read kept failing, and be indistinguishable from a quiet doorstep.
|
|
2564
|
+
* That is the shape of D49, and it has cost this repo real deliveries.
|
|
2565
|
+
*/
|
|
2566
|
+
function motionNearPackageZone(input) {
|
|
2567
|
+
if (input.regions.length === 0) return false;
|
|
2568
|
+
if (input.zoneUnion === void 0) return true;
|
|
2569
|
+
if (input.frameWidth <= 0 || input.frameHeight <= 0) return true;
|
|
2570
|
+
const target = dilateZoneBox(input.zoneUnion);
|
|
2571
|
+
for (const region of input.regions) if (intersects({
|
|
2572
|
+
x: region.bbox.x / input.frameWidth,
|
|
2573
|
+
y: region.bbox.y / input.frameHeight,
|
|
2574
|
+
w: region.bbox.w / input.frameWidth,
|
|
2575
|
+
h: region.bbox.h / input.frameHeight
|
|
2576
|
+
}, target)) return true;
|
|
2577
|
+
return false;
|
|
2578
|
+
}
|
|
2579
|
+
/**
|
|
2580
|
+
* The bounding box of a set of normalised polygons — the package zone union.
|
|
2581
|
+
* Returns `undefined` when there is no usable geometry, which the caller must
|
|
2582
|
+
* pass through as "unknown" rather than as an empty zone.
|
|
2583
|
+
*/
|
|
2584
|
+
function packageZoneUnion(polygons) {
|
|
2585
|
+
let minX = Number.POSITIVE_INFINITY;
|
|
2586
|
+
let minY = Number.POSITIVE_INFINITY;
|
|
2587
|
+
let maxX = Number.NEGATIVE_INFINITY;
|
|
2588
|
+
let maxY = Number.NEGATIVE_INFINITY;
|
|
2589
|
+
let sawPoint = false;
|
|
2590
|
+
for (const polygon of polygons) for (const point of polygon) {
|
|
2591
|
+
sawPoint = true;
|
|
2592
|
+
if (point.x < minX) minX = point.x;
|
|
2593
|
+
if (point.y < minY) minY = point.y;
|
|
2594
|
+
if (point.x > maxX) maxX = point.x;
|
|
2595
|
+
if (point.y > maxY) maxY = point.y;
|
|
2596
|
+
}
|
|
2597
|
+
if (!sawPoint) return void 0;
|
|
2598
|
+
const x = Math.max(0, Math.min(1, minX));
|
|
2599
|
+
const y = Math.max(0, Math.min(1, minY));
|
|
2600
|
+
const w = Math.max(0, Math.min(1, maxX)) - x;
|
|
2601
|
+
const h = Math.max(0, Math.min(1, maxY)) - y;
|
|
2602
|
+
if (w <= 0 || h <= 0) return void 0;
|
|
2603
|
+
return {
|
|
2604
|
+
x,
|
|
2605
|
+
y,
|
|
2606
|
+
w,
|
|
2607
|
+
h
|
|
2608
|
+
};
|
|
2609
|
+
}
|
|
2610
|
+
//#endregion
|
|
2462
2611
|
//#region src/pipeline-runner/native-crop-tier.ts
|
|
2463
2612
|
/**
|
|
2464
2613
|
* Pure tier decision for `getNativeCrop`. A native HIT always resolves to the
|
|
@@ -3836,6 +3985,13 @@ function isUsable(bbox) {
|
|
|
3836
3985
|
//#endregion
|
|
3837
3986
|
//#region src/pipeline-runner/index.ts
|
|
3838
3987
|
/**
|
|
3988
|
+
* How often a device's package zone-union geometry is re-read. An operator
|
|
3989
|
+
* redrawing a package zone takes effect within one interval; the read is a
|
|
3990
|
+
* cross-addon cap call reached from the per-frame package gate, so it must be
|
|
3991
|
+
* throttled to something far slower than the frame rate.
|
|
3992
|
+
*/
|
|
3993
|
+
var PACKAGE_ZONE_UNION_TTL_MS = 6e4;
|
|
3994
|
+
/**
|
|
3839
3995
|
* Bound on the frames whose cutter is bound but whose result has not come back.
|
|
3840
3996
|
* A few per camera in flight; the ceiling only catches results that never
|
|
3841
3997
|
* arrive (shed, inference timeout, detach mid-flight).
|
|
@@ -4206,6 +4362,36 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
4206
4362
|
*/
|
|
4207
4363
|
packageGovernor = new MotionSettleGovernor();
|
|
4208
4364
|
/**
|
|
4365
|
+
* Frames on which the governor DROPPED `package-detection` since that
|
|
4366
|
+
* device last ran it. The drop is the single most consequential silent
|
|
4367
|
+
* branch in the package chain — it decides the cadence that the whole
|
|
4368
|
+
* downstream promotion criterion has to be satisfiable at — and it logged
|
|
4369
|
+
* nothing at all, so "the parcel was never detected" and "the step never
|
|
4370
|
+
* ran" were indistinguishable in the logs. Counted here and reported on the
|
|
4371
|
+
* transition (first skip) and on every run, never per frame.
|
|
4372
|
+
*/
|
|
4373
|
+
packageGateSkips = /* @__PURE__ */ new Map();
|
|
4374
|
+
/**
|
|
4375
|
+
* Per-device package zone-union box (normalised), the target the motion
|
|
4376
|
+
* proximity test measures against.
|
|
4377
|
+
*
|
|
4378
|
+
* A device MISSING from this map is "unknown", not "no zones" — and unknown
|
|
4379
|
+
* FAILS OPEN in `motionNearPackageZone`, degrading to the whole-frame motion
|
|
4380
|
+
* signal. Resolved off the frame path (on attach, and refreshed on the
|
|
4381
|
+
* package-step gate's own cadence), never inside `runMotionAnalysis`.
|
|
4382
|
+
*/
|
|
4383
|
+
packageZoneUnionByDevice = /* @__PURE__ */ new Map();
|
|
4384
|
+
/** In-flight/last zone-union resolution time per device, so the refresh does
|
|
4385
|
+
* not stampede the zone caps from the motion path. */
|
|
4386
|
+
packageZoneUnionAt = /* @__PURE__ */ new Map();
|
|
4387
|
+
/**
|
|
4388
|
+
* Whether the last analyzer frame saw motion NEAR the device's package
|
|
4389
|
+
* zones — the boolean the settle-burst governor consumes. Distinct from
|
|
4390
|
+
* `lastAnalyzerDetected` (whole-frame), which is what the phase machine
|
|
4391
|
+
* uses and which must not change meaning.
|
|
4392
|
+
*/
|
|
4393
|
+
lastMotionNearPackageZone = /* @__PURE__ */ new Map();
|
|
4394
|
+
/**
|
|
4209
4395
|
* Last positive motion timestamp per device — preserved across the
|
|
4210
4396
|
* OFF transition so the motion runtime-state slice keeps a stable
|
|
4211
4397
|
* `lastDetectedAt` after the cooldown closes the phase. Cleared on
|
|
@@ -4406,6 +4592,10 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
4406
4592
|
}
|
|
4407
4593
|
this.lastAnalyzerDetected.clear();
|
|
4408
4594
|
this.packageGovernor.reset();
|
|
4595
|
+
this.packageGateSkips.clear();
|
|
4596
|
+
this.packageZoneUnionByDevice.clear();
|
|
4597
|
+
this.packageZoneUnionAt.clear();
|
|
4598
|
+
this.lastMotionNearPackageZone.clear();
|
|
4409
4599
|
for (const deviceId of [...this.onboardAnalyzerTeardownTimers.keys(), ...this.onboardAnalyzerSubs.keys()]) this.clearOnboardAnalyzer(deviceId);
|
|
4410
4600
|
if (this.runner) {
|
|
4411
4601
|
this.runner.stop();
|
|
@@ -5668,6 +5858,10 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
5668
5858
|
this.lastMotionFrameAt.delete(deviceId);
|
|
5669
5859
|
this.lastDecodeUnexpectedEndAt.delete(deviceId);
|
|
5670
5860
|
this.packageGovernor.forget(deviceId);
|
|
5861
|
+
this.packageGateSkips.delete(deviceId);
|
|
5862
|
+
this.packageZoneUnionByDevice.delete(deviceId);
|
|
5863
|
+
this.packageZoneUnionAt.delete(deviceId);
|
|
5864
|
+
this.lastMotionNearPackageZone.delete(deviceId);
|
|
5671
5865
|
const warnPrefix = `${deviceId}:`;
|
|
5672
5866
|
for (const key of this.warnedNoJumpDevice) if (key.startsWith(warnPrefix)) this.warnedNoJumpDevice.delete(key);
|
|
5673
5867
|
this.attached.delete(deviceId);
|
|
@@ -6244,13 +6438,31 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
6244
6438
|
if (steps.length === 0) return null;
|
|
6245
6439
|
let dispatchSteps = steps;
|
|
6246
6440
|
if (steps.some((s) => s.addonId === "package-detection" && s.enabled !== false)) {
|
|
6247
|
-
|
|
6441
|
+
this.refreshPackageZoneUnion(deviceId);
|
|
6442
|
+
const motionInZone = this.lastMotionNearPackageZone.get(deviceId) ?? this.lastAnalyzerDetected.get(deviceId) ?? false;
|
|
6443
|
+
const decision = this.packageGovernor.evaluate({
|
|
6248
6444
|
deviceId,
|
|
6249
|
-
motionInZone
|
|
6445
|
+
motionInZone,
|
|
6250
6446
|
now: Date.now()
|
|
6251
|
-
})
|
|
6447
|
+
});
|
|
6448
|
+
const skipped = this.packageGateSkips.get(deviceId) ?? 0;
|
|
6449
|
+
if (!decision.run) {
|
|
6252
6450
|
dispatchSteps = steps.filter((s) => s.addonId !== PACKAGE_DETECTION_STEP_ID);
|
|
6451
|
+
if (skipped === 0) log.info("package-detection gated off by the motion-settle governor", {
|
|
6452
|
+
tags: { deviceId },
|
|
6453
|
+
meta: { motionInZone: this.lastAnalyzerDetected.get(deviceId) ?? false }
|
|
6454
|
+
});
|
|
6455
|
+
this.packageGateSkips.set(deviceId, skipped + 1);
|
|
6253
6456
|
if (dispatchSteps.length === 0) return null;
|
|
6457
|
+
} else {
|
|
6458
|
+
this.packageGateSkips.set(deviceId, 0);
|
|
6459
|
+
log.info("package-detection dispatched by the motion-settle governor", {
|
|
6460
|
+
tags: { deviceId },
|
|
6461
|
+
meta: {
|
|
6462
|
+
reason: decision.reason,
|
|
6463
|
+
framesSkippedSinceLastRun: skipped
|
|
6464
|
+
}
|
|
6465
|
+
});
|
|
6254
6466
|
}
|
|
6255
6467
|
}
|
|
6256
6468
|
let image;
|
|
@@ -6299,6 +6511,51 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
6299
6511
|
}
|
|
6300
6512
|
return false;
|
|
6301
6513
|
}
|
|
6514
|
+
/**
|
|
6515
|
+
* Refresh a device's package zone-union box, off the caller's path.
|
|
6516
|
+
*
|
|
6517
|
+
* Cross-addon reads go through `ctx.api` (the only cross-addon layer), and
|
|
6518
|
+
* they are RATE-LIMITED per device: this is reached from the per-frame
|
|
6519
|
+
* package gate, and two cap calls per frame would be a self-inflicted load
|
|
6520
|
+
* problem on the exact cameras the feature exists for.
|
|
6521
|
+
*
|
|
6522
|
+
* A FAILED read leaves the mirror untouched (D49): the last-known-good
|
|
6523
|
+
* geometry keeps being used, and a device that has never been read stays
|
|
6524
|
+
* absent from the map, which `motionNearPackageZone` treats as unknown and
|
|
6525
|
+
* fails OPEN on. Nothing here can make package detection quieter.
|
|
6526
|
+
*/
|
|
6527
|
+
refreshPackageZoneUnion(deviceId) {
|
|
6528
|
+
const now = Date.now();
|
|
6529
|
+
if (now - (this.packageZoneUnionAt.get(deviceId) ?? 0) < PACKAGE_ZONE_UNION_TTL_MS) return;
|
|
6530
|
+
this.packageZoneUnionAt.set(deviceId, now);
|
|
6531
|
+
const api = this.ctx.api;
|
|
6532
|
+
if (!api) return;
|
|
6533
|
+
(async () => {
|
|
6534
|
+
try {
|
|
6535
|
+
const [zones, rules] = await Promise.all([api.zones.listZones.query({ deviceId }), api.zoneRules.listRules.query({
|
|
6536
|
+
deviceId,
|
|
6537
|
+
stage: "package"
|
|
6538
|
+
})]);
|
|
6539
|
+
const zoneIds = new Set(rules.filter((r) => r.enabled !== false).flatMap((r) => r.zoneIds));
|
|
6540
|
+
const polygons = zones.filter((z) => zoneIds.has(z.id)).map((z) => z.polygon);
|
|
6541
|
+
const union = packageZoneUnion(polygons);
|
|
6542
|
+
const previous = this.packageZoneUnionByDevice.get(deviceId);
|
|
6543
|
+
this.packageZoneUnionByDevice.set(deviceId, union);
|
|
6544
|
+
if (previous === void 0 && union !== void 0) this.ctx.logger.info("package zone geometry resolved — motion proximity armed", {
|
|
6545
|
+
tags: { deviceId },
|
|
6546
|
+
meta: {
|
|
6547
|
+
zoneUnion: union,
|
|
6548
|
+
zones: polygons.length
|
|
6549
|
+
}
|
|
6550
|
+
});
|
|
6551
|
+
} catch (err) {
|
|
6552
|
+
this.ctx.logger.warn("package zone geometry read failed — motion proximity fails open", {
|
|
6553
|
+
tags: { deviceId },
|
|
6554
|
+
meta: { error: err instanceof Error ? err.message : String(err) }
|
|
6555
|
+
});
|
|
6556
|
+
}
|
|
6557
|
+
})();
|
|
6558
|
+
}
|
|
6302
6559
|
async runMotionAnalysis(deviceId, frame, handle) {
|
|
6303
6560
|
const ctx = this.ctx;
|
|
6304
6561
|
const runner = this.runner;
|
|
@@ -6319,6 +6576,12 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
6319
6576
|
});
|
|
6320
6577
|
if (!result) return;
|
|
6321
6578
|
const detected = result.regions.length > 0;
|
|
6579
|
+
this.lastMotionNearPackageZone.set(deviceId, motionNearPackageZone({
|
|
6580
|
+
regions: result.regions,
|
|
6581
|
+
frameWidth: result.frameWidth,
|
|
6582
|
+
frameHeight: result.frameHeight,
|
|
6583
|
+
zoneUnion: this.packageZoneUnionByDevice.get(deviceId)
|
|
6584
|
+
}));
|
|
6322
6585
|
if (detected !== (this.lastAnalyzerDetected.get(deviceId) ?? false)) {
|
|
6323
6586
|
this.lastAnalyzerDetected.set(deviceId, detected);
|
|
6324
6587
|
if (this.ctx.eventBus) this.ctx.eventBus.emit(require_dist.createEvent(require_dist.EventCategory.MotionOnMotionChanged, {
|