@camstack/addon-post-analysis 1.2.212 → 1.2.214
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/{dist-GQbzNwJB.mjs → dist-CT2SouNJ.mjs} +42 -1
- package/dist/{dist-IE6UOohI.js → dist-DA1sJKXM.js} +42 -1
- package/dist/embedding-encoder/index.js +1 -1
- package/dist/embedding-encoder/index.mjs +1 -1
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-utVZjlcV.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-TUlCd-Zk.mjs} +3 -3
- package/dist/pipeline-analytics/{hostInit-BL9mWOe9.mjs → hostInit-CN3MIGfb.mjs} +3 -3
- package/dist/pipeline-analytics/index.js +209 -9
- package/dist/pipeline-analytics/index.mjs +209 -9
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -1
|
@@ -22483,6 +22483,22 @@ var occupancyRecheckFramesField = {
|
|
|
22483
22483
|
step: 1
|
|
22484
22484
|
};
|
|
22485
22485
|
/**
|
|
22486
|
+
* Frames one PRE-ROLL catch-up burst may send to inference at session open.
|
|
22487
|
+
*
|
|
22488
|
+
* The default of 12 over a ~4 s retained window is one frame per ~333 ms of
|
|
22489
|
+
* footage, and costs ~480 ms of ONE inference permit at the 40 ms/frame this
|
|
22490
|
+
* fleet measures — one-shot, and only ever taken while a permit would still be
|
|
22491
|
+
* spare for live. `0` turns the burst off entirely (the history is still
|
|
22492
|
+
* decoded, because that is how the dial reaches a decodable GOP, and is then
|
|
22493
|
+
* discarded — the pre-D411 behaviour).
|
|
22494
|
+
*/
|
|
22495
|
+
var preRollInferenceFramesField = {
|
|
22496
|
+
min: 0,
|
|
22497
|
+
max: 30,
|
|
22498
|
+
default: 12,
|
|
22499
|
+
step: 1
|
|
22500
|
+
};
|
|
22501
|
+
/**
|
|
22486
22502
|
* Source enum for motion signals fed to the runner. Extensible — add
|
|
22487
22503
|
* new variants here when new motion-trigger paths are wired in
|
|
22488
22504
|
* (`wasm-cross-camera`, `event-bus-relay`, etc.). The runner uses
|
|
@@ -22723,6 +22739,31 @@ var RunnerCameraConfigSchema = object({
|
|
|
22723
22739
|
* to every occupancy rule until something moved in front of it. The churn is
|
|
22724
22740
|
* now paid on the interval instead — see `occupancyRecheckSecField`.
|
|
22725
22741
|
*/
|
|
22742
|
+
/**
|
|
22743
|
+
* Infer the session's PRE-ROLL — the retained history the restreamer replays
|
|
22744
|
+
* at session open — instead of decoding it and throwing it away.
|
|
22745
|
+
*
|
|
22746
|
+
* DEFAULT `true`, and the default is the argument. This is not a new
|
|
22747
|
+
* capability being offered cautiously: the pre-roll is ALREADY requested,
|
|
22748
|
+
* already served, already decoded and already paid for on every on-motion
|
|
22749
|
+
* detection session that asks for history. What shipped was a last-moment
|
|
22750
|
+
* discard — `FrameSlot`'s throttle compares wall clocks, and ~4 s of media
|
|
22751
|
+
* arriving inside a few hundred ms of wall clock looks to it like one frame's
|
|
22752
|
+
* worth. Device 617, 2026-09-08: a re-run of the SAME model over the stored
|
|
22753
|
+
* recording scored the subject `vehicle 0.5669` — above the 0.50 floor —
|
|
22754
|
+
* 1.68 s BEFORE the live track was born, on a frame that was in the pre-roll,
|
|
22755
|
+
* was decoded, and was freed.
|
|
22756
|
+
*
|
|
22757
|
+
* Shipping that as opt-in would ship the bug: an operator cannot discover a
|
|
22758
|
+
* setting whose absence looks exactly like a camera that noticed the cyclist
|
|
22759
|
+
* late. What makes ON safe is not a switch but the BOUND —
|
|
22760
|
+
* `preRollInferenceFrames`, a wall-clock ceiling, and a lane that never takes
|
|
22761
|
+
* the last inference permit — so the switch exists for the camera where the
|
|
22762
|
+
* history is worthless (a doorbell whose subject is always already at the
|
|
22763
|
+
* door), not as a hedge against the feature.
|
|
22764
|
+
*/
|
|
22765
|
+
preRollInferenceEnabled: boolean().default(true),
|
|
22766
|
+
preRollInferenceFrames: number().min(preRollInferenceFramesField.min).max(preRollInferenceFramesField.max).default(preRollInferenceFramesField.default),
|
|
22726
22767
|
occupancyRecheckEnabled: boolean().default(true),
|
|
22727
22768
|
occupancyRecheckSec: number().min(occupancyRecheckSecField.min).max(occupancyRecheckSecField.max).default(occupancyRecheckSecField.default),
|
|
22728
22769
|
occupancyRecheckFrames: number().min(occupancyRecheckFramesField.min).max(occupancyRecheckFramesField.max).default(occupancyRecheckFramesField.default),
|
|
@@ -22751,7 +22792,7 @@ var RunnerCameraConfigSchema = object({
|
|
|
22751
22792
|
*/
|
|
22752
22793
|
inferenceDevices: array(RunnerInferenceDeviceSchema).readonly().optional()
|
|
22753
22794
|
});
|
|
22754
|
-
motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, audioMotionWindowMsField.min, audioMotionWindowMsField.max, audioMotionWindowMsField.step, audioMotionWindowMsField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
|
|
22795
|
+
motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, audioMotionWindowMsField.min, audioMotionWindowMsField.max, audioMotionWindowMsField.step, audioMotionWindowMsField.default, preRollInferenceFramesField.min, preRollInferenceFramesField.max, preRollInferenceFramesField.step, preRollInferenceFramesField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
|
|
22755
22796
|
/**
|
|
22756
22797
|
* Runtime load summary returned by `getLocalLoad`. Used by the orchestrator's
|
|
22757
22798
|
* load-balancing levels (L2 capacity-based, L3 hardware-aware) to decide
|
|
@@ -22514,6 +22514,22 @@ var occupancyRecheckFramesField = {
|
|
|
22514
22514
|
step: 1
|
|
22515
22515
|
};
|
|
22516
22516
|
/**
|
|
22517
|
+
* Frames one PRE-ROLL catch-up burst may send to inference at session open.
|
|
22518
|
+
*
|
|
22519
|
+
* The default of 12 over a ~4 s retained window is one frame per ~333 ms of
|
|
22520
|
+
* footage, and costs ~480 ms of ONE inference permit at the 40 ms/frame this
|
|
22521
|
+
* fleet measures — one-shot, and only ever taken while a permit would still be
|
|
22522
|
+
* spare for live. `0` turns the burst off entirely (the history is still
|
|
22523
|
+
* decoded, because that is how the dial reaches a decodable GOP, and is then
|
|
22524
|
+
* discarded — the pre-D411 behaviour).
|
|
22525
|
+
*/
|
|
22526
|
+
var preRollInferenceFramesField = {
|
|
22527
|
+
min: 0,
|
|
22528
|
+
max: 30,
|
|
22529
|
+
default: 12,
|
|
22530
|
+
step: 1
|
|
22531
|
+
};
|
|
22532
|
+
/**
|
|
22517
22533
|
* Source enum for motion signals fed to the runner. Extensible — add
|
|
22518
22534
|
* new variants here when new motion-trigger paths are wired in
|
|
22519
22535
|
* (`wasm-cross-camera`, `event-bus-relay`, etc.). The runner uses
|
|
@@ -22754,6 +22770,31 @@ var RunnerCameraConfigSchema = object({
|
|
|
22754
22770
|
* to every occupancy rule until something moved in front of it. The churn is
|
|
22755
22771
|
* now paid on the interval instead — see `occupancyRecheckSecField`.
|
|
22756
22772
|
*/
|
|
22773
|
+
/**
|
|
22774
|
+
* Infer the session's PRE-ROLL — the retained history the restreamer replays
|
|
22775
|
+
* at session open — instead of decoding it and throwing it away.
|
|
22776
|
+
*
|
|
22777
|
+
* DEFAULT `true`, and the default is the argument. This is not a new
|
|
22778
|
+
* capability being offered cautiously: the pre-roll is ALREADY requested,
|
|
22779
|
+
* already served, already decoded and already paid for on every on-motion
|
|
22780
|
+
* detection session that asks for history. What shipped was a last-moment
|
|
22781
|
+
* discard — `FrameSlot`'s throttle compares wall clocks, and ~4 s of media
|
|
22782
|
+
* arriving inside a few hundred ms of wall clock looks to it like one frame's
|
|
22783
|
+
* worth. Device 617, 2026-09-08: a re-run of the SAME model over the stored
|
|
22784
|
+
* recording scored the subject `vehicle 0.5669` — above the 0.50 floor —
|
|
22785
|
+
* 1.68 s BEFORE the live track was born, on a frame that was in the pre-roll,
|
|
22786
|
+
* was decoded, and was freed.
|
|
22787
|
+
*
|
|
22788
|
+
* Shipping that as opt-in would ship the bug: an operator cannot discover a
|
|
22789
|
+
* setting whose absence looks exactly like a camera that noticed the cyclist
|
|
22790
|
+
* late. What makes ON safe is not a switch but the BOUND —
|
|
22791
|
+
* `preRollInferenceFrames`, a wall-clock ceiling, and a lane that never takes
|
|
22792
|
+
* the last inference permit — so the switch exists for the camera where the
|
|
22793
|
+
* history is worthless (a doorbell whose subject is always already at the
|
|
22794
|
+
* door), not as a hedge against the feature.
|
|
22795
|
+
*/
|
|
22796
|
+
preRollInferenceEnabled: boolean().default(true),
|
|
22797
|
+
preRollInferenceFrames: number().min(preRollInferenceFramesField.min).max(preRollInferenceFramesField.max).default(preRollInferenceFramesField.default),
|
|
22757
22798
|
occupancyRecheckEnabled: boolean().default(true),
|
|
22758
22799
|
occupancyRecheckSec: number().min(occupancyRecheckSecField.min).max(occupancyRecheckSecField.max).default(occupancyRecheckSecField.default),
|
|
22759
22800
|
occupancyRecheckFrames: number().min(occupancyRecheckFramesField.min).max(occupancyRecheckFramesField.max).default(occupancyRecheckFramesField.default),
|
|
@@ -22782,7 +22823,7 @@ var RunnerCameraConfigSchema = object({
|
|
|
22782
22823
|
*/
|
|
22783
22824
|
inferenceDevices: array(RunnerInferenceDeviceSchema).readonly().optional()
|
|
22784
22825
|
});
|
|
22785
|
-
motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, audioMotionWindowMsField.min, audioMotionWindowMsField.max, audioMotionWindowMsField.step, audioMotionWindowMsField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
|
|
22826
|
+
motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, audioMotionWindowMsField.min, audioMotionWindowMsField.max, audioMotionWindowMsField.step, audioMotionWindowMsField.default, preRollInferenceFramesField.min, preRollInferenceFramesField.max, preRollInferenceFramesField.step, preRollInferenceFramesField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
|
|
22786
22827
|
/**
|
|
22787
22828
|
* Runtime load summary returned by `getLocalLoad`. Used by the orchestrator's
|
|
22788
22829
|
* load-balancing levels (L2 capacity-based, L3 hardware-aware) to decide
|
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
5
|
+
const require_dist = require("../dist-DA1sJKXM.js");
|
|
6
6
|
let node_fs = require("node:fs");
|
|
7
7
|
node_fs = require_dist.__toESM(node_fs);
|
|
8
8
|
let node_path = require("node:path");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { At as resolvePoolMemoryPolicy, B as PoolMemoryWatchdog, Ct as parseProcStatus, gt as hfModelUrl, ut as embeddingEncoderCapability, zt as BaseAddon } from "../dist-
|
|
1
|
+
import { At as resolvePoolMemoryPolicy, B as PoolMemoryWatchdog, Ct as parseProcStatus, gt as hfModelUrl, ut as embeddingEncoderCapability, zt as BaseAddon } from "../dist-CT2SouNJ.mjs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
@@ -3,7 +3,7 @@ import "./dist-CYZr2fwk.mjs";
|
|
|
3
3
|
var e = {
|
|
4
4
|
"@camstack/sdk": {
|
|
5
5
|
name: "@camstack/sdk",
|
|
6
|
-
version: "1.2.
|
|
6
|
+
version: "1.2.93",
|
|
7
7
|
scope: ["default"],
|
|
8
8
|
loaded: !1,
|
|
9
9
|
from: "addon_pipeline_analytics_widgets",
|
|
@@ -18,7 +18,7 @@ var e = {
|
|
|
18
18
|
},
|
|
19
19
|
"@camstack/types": {
|
|
20
20
|
name: "@camstack/types",
|
|
21
|
-
version: "1.2.
|
|
21
|
+
version: "1.2.173",
|
|
22
22
|
scope: ["default"],
|
|
23
23
|
loaded: !1,
|
|
24
24
|
from: "addon_pipeline_analytics_widgets",
|
|
@@ -33,7 +33,7 @@ var e = {
|
|
|
33
33
|
},
|
|
34
34
|
"@camstack/ui-library": {
|
|
35
35
|
name: "@camstack/ui-library",
|
|
36
|
-
version: "1.2.
|
|
36
|
+
version: "1.2.141",
|
|
37
37
|
scope: ["default"],
|
|
38
38
|
loaded: !1,
|
|
39
39
|
from: "addon_pipeline_analytics_widgets",
|
|
@@ -36,7 +36,7 @@ async function r() {
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"@camstack/types": {
|
|
39
|
-
version: "1.2.
|
|
39
|
+
version: "1.2.173",
|
|
40
40
|
scope: "default",
|
|
41
41
|
shareConfig: {
|
|
42
42
|
singleton: !0,
|
|
@@ -45,7 +45,7 @@ async function r() {
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
"@camstack/sdk": {
|
|
48
|
-
version: "1.2.
|
|
48
|
+
version: "1.2.93",
|
|
49
49
|
scope: "default",
|
|
50
50
|
shareConfig: {
|
|
51
51
|
singleton: !0,
|
|
@@ -81,7 +81,7 @@ async function r() {
|
|
|
81
81
|
}
|
|
82
82
|
},
|
|
83
83
|
"@camstack/ui-library": {
|
|
84
|
-
version: "1.2.
|
|
84
|
+
version: "1.2.141",
|
|
85
85
|
scope: "default",
|
|
86
86
|
shareConfig: {
|
|
87
87
|
singleton: !0,
|
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
5
|
+
const require_dist = require("../dist-DA1sJKXM.js");
|
|
6
6
|
let node_fs = require("node:fs");
|
|
7
7
|
node_fs = require_dist.__toESM(node_fs, 1);
|
|
8
8
|
let node_path = require("node:path");
|
|
@@ -10,6 +10,7 @@ let node_path$1 = require_dist.__toESM(node_path, 1);
|
|
|
10
10
|
node_path = require_dist.__toESM(node_path);
|
|
11
11
|
let node_crypto = require("node:crypto");
|
|
12
12
|
let node_child_process = require("node:child_process");
|
|
13
|
+
let _camstack_system_addon_utils = require("@camstack/system/addon-utils");
|
|
13
14
|
let sharp = require("sharp");
|
|
14
15
|
sharp = require_dist.__toESM(sharp);
|
|
15
16
|
let node_os = require("node:os");
|
|
@@ -34861,6 +34862,9 @@ var DEFAULT_TRACKER_CONFIG = {
|
|
|
34861
34862
|
var PERSON_CLASS = "person";
|
|
34862
34863
|
var ANIMAL_CLASS = "animal";
|
|
34863
34864
|
var MAX_PATH_LENGTH = 300;
|
|
34865
|
+
/** No stationary-held detections on this frame — the default for every caller
|
|
34866
|
+
* that has no gate (replay, tests, the non-pipeline planes). */
|
|
34867
|
+
var EMPTY_INDEX_SET = /* @__PURE__ */ new Set();
|
|
34864
34868
|
function clamp$1(value, min, max) {
|
|
34865
34869
|
return Math.max(min, Math.min(max, value));
|
|
34866
34870
|
}
|
|
@@ -35480,13 +35484,15 @@ var SortTracker = class SortTracker {
|
|
|
35480
35484
|
* gates) so the two-tier ByteTrack association stays a single implementation.
|
|
35481
35485
|
* Mutates `used` / `matchedTracks` / `matched` in place.
|
|
35482
35486
|
*/
|
|
35483
|
-
greedyAssociate(detections, candidateIdxs, iouThreshold, timestamp, used, matchedTracks, matched) {
|
|
35487
|
+
greedyAssociate(detections, candidateIdxs, iouThreshold, timestamp, used, matchedTracks, matched, continuationOnly) {
|
|
35484
35488
|
const pairs = [];
|
|
35485
35489
|
for (const track of this.tracks) {
|
|
35486
35490
|
if (matchedTracks.has(track)) continue;
|
|
35487
35491
|
const pbox = this.predicted(track, timestamp);
|
|
35492
|
+
const confirmed = this.isConfirmedForEmit(track);
|
|
35488
35493
|
for (const di of candidateIdxs) {
|
|
35489
35494
|
if (used.has(di)) continue;
|
|
35495
|
+
if (!confirmed && continuationOnly.has(di)) continue;
|
|
35490
35496
|
const det = detections[di];
|
|
35491
35497
|
if (!this.associable(track.class, det)) continue;
|
|
35492
35498
|
const score = iou$3(pbox, det.bbox);
|
|
@@ -35507,6 +35513,7 @@ var SortTracker = class SortTracker {
|
|
|
35507
35513
|
}
|
|
35508
35514
|
update(detections, timestamp, frameContext) {
|
|
35509
35515
|
const frameArea = frameContext ? frameContext.frameWidth * frameContext.frameHeight : 0;
|
|
35516
|
+
const continuationOnly = frameContext?.continuationOnly ?? EMPTY_INDEX_SET;
|
|
35510
35517
|
const resurrectedScene = /* @__PURE__ */ new Map();
|
|
35511
35518
|
if (this.config.maxTrackLifetimeMs > 0) {
|
|
35512
35519
|
const alive = [];
|
|
@@ -35543,12 +35550,12 @@ var SortTracker = class SortTracker {
|
|
|
35543
35550
|
for (let di = 0; di < detections.length; di++) if (this.config.byteTrackEnabled && detections[di].score < this.config.byteTrackHighThreshold) lowIdxs.push(di);
|
|
35544
35551
|
else highIdxs.push(di);
|
|
35545
35552
|
const lowSet = new Set(lowIdxs);
|
|
35546
|
-
this.greedyAssociate(detections, highIdxs, this.config.iouThreshold, timestamp, used, matchedTracks, matched);
|
|
35553
|
+
this.greedyAssociate(detections, highIdxs, this.config.iouThreshold, timestamp, used, matchedTracks, matched, continuationOnly);
|
|
35547
35554
|
const rescuePairs = [];
|
|
35548
35555
|
for (const track of this.tracks) {
|
|
35549
35556
|
if (matchedTracks.has(track)) continue;
|
|
35550
35557
|
for (let di = 0; di < detections.length; di++) {
|
|
35551
|
-
if (used.has(di) || lowSet.has(di)) continue;
|
|
35558
|
+
if (used.has(di) || lowSet.has(di) || continuationOnly.has(di)) continue;
|
|
35552
35559
|
const det = detections[di];
|
|
35553
35560
|
const gate = this.looseMatch(track, det, timestamp);
|
|
35554
35561
|
if (gate === null) continue;
|
|
@@ -35570,7 +35577,7 @@ var SortTracker = class SortTracker {
|
|
|
35570
35577
|
used.add(pair.detIdx);
|
|
35571
35578
|
this.reportBind("rescue", pair.gate, pair.track, det, timestamp);
|
|
35572
35579
|
}
|
|
35573
|
-
if (this.config.byteTrackEnabled && lowIdxs.length > 0) this.greedyAssociate(detections, lowIdxs, this.config.byteTrackLowIouThreshold, timestamp, used, matchedTracks, matched);
|
|
35580
|
+
if (this.config.byteTrackEnabled && lowIdxs.length > 0) this.greedyAssociate(detections, lowIdxs, this.config.byteTrackLowIouThreshold, timestamp, used, matchedTracks, matched, continuationOnly);
|
|
35574
35581
|
for (const [track, det] of matched) {
|
|
35575
35582
|
if (track.class !== det.class) this.reportRefusal("cross-class-absorbed", det, { against: track });
|
|
35576
35583
|
const prevCenter = bboxCentroid({
|
|
@@ -35626,7 +35633,7 @@ var SortTracker = class SortTracker {
|
|
|
35626
35633
|
} else surviving.push(track);
|
|
35627
35634
|
}
|
|
35628
35635
|
for (let di = 0; di < detections.length; di++) {
|
|
35629
|
-
if (used.has(di)) continue;
|
|
35636
|
+
if (used.has(di) || continuationOnly.has(di)) continue;
|
|
35630
35637
|
const det = detections[di];
|
|
35631
35638
|
let best;
|
|
35632
35639
|
let bestScore = -1;
|
|
@@ -35694,6 +35701,11 @@ var SortTracker = class SortTracker {
|
|
|
35694
35701
|
});
|
|
35695
35702
|
for (const di of unmatchedIdx) {
|
|
35696
35703
|
const det = detections[di];
|
|
35704
|
+
if (continuationOnly.has(di)) {
|
|
35705
|
+
this.reportRefusal("stationary-hold", det);
|
|
35706
|
+
used.add(di);
|
|
35707
|
+
continue;
|
|
35708
|
+
}
|
|
35697
35709
|
if (!this.meetsClassScoreFloor(det)) {
|
|
35698
35710
|
this.reportRefusal("score-floor", det, {
|
|
35699
35711
|
threshold: this.config.classMinScores[det.class] ?? 0,
|
|
@@ -36432,14 +36444,28 @@ var FrameProcessor = class {
|
|
|
36432
36444
|
timestamp
|
|
36433
36445
|
}) : {
|
|
36434
36446
|
suppressedIndices: /* @__PURE__ */ new Set(),
|
|
36447
|
+
continuableIndices: /* @__PURE__ */ new Set(),
|
|
36435
36448
|
confirmed: [],
|
|
36436
36449
|
woken: []
|
|
36437
36450
|
};
|
|
36438
|
-
const trackerInput =
|
|
36451
|
+
const trackerInput = [];
|
|
36452
|
+
const continuationOnly = /* @__PURE__ */ new Set();
|
|
36453
|
+
const continuableBoxes = /* @__PURE__ */ new Set();
|
|
36454
|
+
for (let i = 0; i < filteredDetections.length; i++) {
|
|
36455
|
+
const det = filteredDetections[i];
|
|
36456
|
+
if (gate.suppressedIndices.has(i)) {
|
|
36457
|
+
if (!gate.continuableIndices.has(i)) continue;
|
|
36458
|
+
continuationOnly.add(trackerInput.length);
|
|
36459
|
+
continuableBoxes.add(det.bbox);
|
|
36460
|
+
}
|
|
36461
|
+
trackerInput.push(det);
|
|
36462
|
+
}
|
|
36439
36463
|
const trackedDetections = this.tracker.update(trackerInput, timestamp, {
|
|
36440
36464
|
frameWidth,
|
|
36441
|
-
frameHeight
|
|
36465
|
+
frameHeight,
|
|
36466
|
+
continuationOnly
|
|
36442
36467
|
});
|
|
36468
|
+
const stationaryContinued = continuableBoxes.size === 0 ? 0 : trackedDetections.filter((t) => t.matchedThisFrame !== false && continuableBoxes.has(t.bbox)).length;
|
|
36443
36469
|
const riderFolds = foldByVehicleBbox.size === 0 ? EMPTY_RIDER_FOLDS : [...foldByVehicleBbox].map(([bbox, fold]) => {
|
|
36444
36470
|
const td = trackedDetections.find((t) => t.bbox === bbox && t.matchedThisFrame !== false);
|
|
36445
36471
|
return td !== void 0 ? {
|
|
@@ -36643,6 +36669,7 @@ var FrameProcessor = class {
|
|
|
36643
36669
|
rawTrackedDetections: trackedDetections,
|
|
36644
36670
|
stationaryConfirmed: gate.confirmed,
|
|
36645
36671
|
stationaryWoken: gate.woken,
|
|
36672
|
+
stationaryContinued,
|
|
36646
36673
|
riderFolds
|
|
36647
36674
|
};
|
|
36648
36675
|
}
|
|
@@ -49534,6 +49561,87 @@ var FaceRecognizer = class {
|
|
|
49534
49561
|
}
|
|
49535
49562
|
}
|
|
49536
49563
|
};
|
|
49564
|
+
/** Read the window. Pure — this is the judgement the module exists to make. */
|
|
49565
|
+
function lagVerdict(report) {
|
|
49566
|
+
const bus = report.busLagMaxMs;
|
|
49567
|
+
if (bus === null) return "unknown";
|
|
49568
|
+
return bus >= 5e3 ? "starved" : "delivered-on-time";
|
|
49569
|
+
}
|
|
49570
|
+
/** Does this window need an operator's eyes? Unknown never counts as alarming
|
|
49571
|
+
* — that is the fabricated verdict D8 forbids. */
|
|
49572
|
+
function isAlarmingLagReport(report) {
|
|
49573
|
+
const bus = report.busLagMaxMs;
|
|
49574
|
+
const capture = report.captureLagMaxMs;
|
|
49575
|
+
return bus !== null && bus >= 5e3 || capture !== null && capture >= 5e3;
|
|
49576
|
+
}
|
|
49577
|
+
/**
|
|
49578
|
+
* Folds per-frame delivery lag into per-device windows.
|
|
49579
|
+
*
|
|
49580
|
+
* Not a logger: it returns the line to write (or null) and the caller decides
|
|
49581
|
+
* level and tags. That keeps the throttle — the part that can wreck the frame
|
|
49582
|
+
* path — testable without a logger fake.
|
|
49583
|
+
*/
|
|
49584
|
+
var FrameLagObserver = class {
|
|
49585
|
+
windows = /* @__PURE__ */ new Map();
|
|
49586
|
+
/**
|
|
49587
|
+
* Fold one inference result into `deviceId`'s window.
|
|
49588
|
+
*
|
|
49589
|
+
* @param now Receipt time, read by the caller once at handler entry.
|
|
49590
|
+
* @param emittedAt Runner-side emit stamp; undefined ⇒ unknown (D8).
|
|
49591
|
+
* @param capturedAt Frame shm-commit stamp; undefined ⇒ unknown.
|
|
49592
|
+
* @returns The closed window when this frame ends one, else null.
|
|
49593
|
+
*/
|
|
49594
|
+
observe(deviceId, now, emittedAt, capturedAt) {
|
|
49595
|
+
const existing = this.windows.get(deviceId);
|
|
49596
|
+
const w = existing ?? {
|
|
49597
|
+
startedAt: now,
|
|
49598
|
+
frames: 0,
|
|
49599
|
+
busMax: null,
|
|
49600
|
+
busMin: null,
|
|
49601
|
+
busUnknown: 0,
|
|
49602
|
+
captureMax: null,
|
|
49603
|
+
captureUnknown: 0
|
|
49604
|
+
};
|
|
49605
|
+
if (existing === void 0) this.windows.set(deviceId, w);
|
|
49606
|
+
w.frames += 1;
|
|
49607
|
+
if (typeof emittedAt === "number") {
|
|
49608
|
+
const busLag = now - emittedAt;
|
|
49609
|
+
if (w.busMax === null || busLag > w.busMax) w.busMax = busLag;
|
|
49610
|
+
if (w.busMin === null || busLag < w.busMin) w.busMin = busLag;
|
|
49611
|
+
} else w.busUnknown += 1;
|
|
49612
|
+
if (typeof capturedAt === "number") {
|
|
49613
|
+
const captureLag = now - capturedAt;
|
|
49614
|
+
if (w.captureMax === null || captureLag > w.captureMax) w.captureMax = captureLag;
|
|
49615
|
+
} else w.captureUnknown += 1;
|
|
49616
|
+
if (existing !== void 0 && now - w.startedAt < 3e4) return null;
|
|
49617
|
+
const report = {
|
|
49618
|
+
deviceId,
|
|
49619
|
+
frames: w.frames,
|
|
49620
|
+
busLagMaxMs: w.busMax,
|
|
49621
|
+
busLagMinMs: w.busMin,
|
|
49622
|
+
busLagUnknownFrames: w.busUnknown,
|
|
49623
|
+
captureLagMaxMs: w.captureMax,
|
|
49624
|
+
captureLagUnknownFrames: w.captureUnknown,
|
|
49625
|
+
windowMs: now - w.startedAt
|
|
49626
|
+
};
|
|
49627
|
+
w.startedAt = now;
|
|
49628
|
+
w.frames = 0;
|
|
49629
|
+
w.busMax = null;
|
|
49630
|
+
w.busMin = null;
|
|
49631
|
+
w.busUnknown = 0;
|
|
49632
|
+
w.captureMax = null;
|
|
49633
|
+
w.captureUnknown = 0;
|
|
49634
|
+
return report;
|
|
49635
|
+
}
|
|
49636
|
+
/** Drop a device's window (unbound camera, shutdown). */
|
|
49637
|
+
forget(deviceId) {
|
|
49638
|
+
this.windows.delete(deviceId);
|
|
49639
|
+
}
|
|
49640
|
+
/** Drop every window. */
|
|
49641
|
+
clear() {
|
|
49642
|
+
this.windows.clear();
|
|
49643
|
+
}
|
|
49644
|
+
};
|
|
49537
49645
|
//#endregion
|
|
49538
49646
|
//#region src/pipeline-analytics/location-aware-media-storage.ts
|
|
49539
49647
|
/**
|
|
@@ -54989,6 +55097,31 @@ function thumbnailWouldShowParkedNotSubject(input) {
|
|
|
54989
55097
|
* dropped, against every entry that did NOT wake this frame. Woken entries are
|
|
54990
55098
|
* excluded so a departure still releases its object — a car that drives away is
|
|
54991
55099
|
* noticed on the frame its second read lands, exactly as before.
|
|
55100
|
+
*
|
|
55101
|
+
* ## A weak hold may CONTINUE a track; only a strong read is the object (D410)
|
|
55102
|
+
*
|
|
55103
|
+
* D275 predicted that a moving vehicle overlapping a parked one "loses a frame
|
|
55104
|
+
* or two while it passes" and coasts through on `maxAge`. Measured on device
|
|
55105
|
+
* 617 on 2026-09-08 it lost the whole pass: the parked cars sit at the frame
|
|
55106
|
+
* edge, so a car driving across their front is held on every frame from the
|
|
55107
|
+
* first overlap to the moment it leaves the frame, and the tracker — which had
|
|
55108
|
+
* followed it for 5–10 frames — coasts `maxMissedMs` on nothing and drops it.
|
|
55109
|
+
* Four operator-marked tracks, 1.7–3.5 s old, died on exactly that geometry;
|
|
55110
|
+
* the sweep for the window reported `reach: 0`, so the hold was pass 1's: the
|
|
55111
|
+
* mover occludes the parked car, the detector reports one vehicle box, and it
|
|
55112
|
+
* is the entry's best match.
|
|
55113
|
+
*
|
|
55114
|
+
* The hold is right — that box covers the parked spot and must not OPEN a
|
|
55115
|
+
* track, which is the whole of the operator's requirement — but it is not the
|
|
55116
|
+
* parked car: a strong read (IoU ≥ `suppressIou`) is the parked object unmoved,
|
|
55117
|
+
* and everything below it is a box that merely overlaps the spot (swallow,
|
|
55118
|
+
* jitter, departing). So `suppressedIndices` still names every box that may
|
|
55119
|
+
* not spawn, and `continuableIndices` names the weak subset the tracker may use
|
|
55120
|
+
* to CONTINUE a track it has already confirmed — through its strict
|
|
55121
|
+
* predicted-box association only, never a rescue or a resurrection. The reach
|
|
55122
|
+
* pass reports strength the same way. Nothing about confirmation, anchoring or
|
|
55123
|
+
* departure changes: a weak hold confirms and never re-anchors, exactly as
|
|
55124
|
+
* before.
|
|
54992
55125
|
*/
|
|
54993
55126
|
function partitionDetectionsAgainstRegistry(input) {
|
|
54994
55127
|
const { entries, detections, config, now } = input;
|
|
@@ -54996,6 +55129,7 @@ function partitionDetectionsAgainstRegistry(input) {
|
|
|
54996
55129
|
const departure = input.departure ?? DEFAULT_DEPARTURE_CONFIG;
|
|
54997
55130
|
const warmUp = input.warmUp ?? false;
|
|
54998
55131
|
const suppressed = /* @__PURE__ */ new Set();
|
|
55132
|
+
const continuable = /* @__PURE__ */ new Set();
|
|
54999
55133
|
const confirmed = [];
|
|
55000
55134
|
const woken = [];
|
|
55001
55135
|
const strikeChanges = [];
|
|
@@ -55035,6 +55169,7 @@ function partitionDetectionsAgainstRegistry(input) {
|
|
|
55035
55169
|
}
|
|
55036
55170
|
if (holdIdx >= 0) {
|
|
55037
55171
|
suppressed.add(holdIdx);
|
|
55172
|
+
if (!strong) continuable.add(holdIdx);
|
|
55038
55173
|
confirmed.push({
|
|
55039
55174
|
entryId: entry.id,
|
|
55040
55175
|
className: entry.className,
|
|
@@ -55074,6 +55209,7 @@ function partitionDetectionsAgainstRegistry(input) {
|
|
|
55074
55209
|
if (warmUp) {
|
|
55075
55210
|
warmUpDepartureReads += 1;
|
|
55076
55211
|
suppressed.add(bestIdx);
|
|
55212
|
+
continuable.add(bestIdx);
|
|
55077
55213
|
continue;
|
|
55078
55214
|
}
|
|
55079
55215
|
if (strike === void 0) {
|
|
@@ -55086,6 +55222,7 @@ function partitionDetectionsAgainstRegistry(input) {
|
|
|
55086
55222
|
reason: "armed"
|
|
55087
55223
|
});
|
|
55088
55224
|
suppressed.add(bestIdx);
|
|
55225
|
+
continuable.add(bestIdx);
|
|
55089
55226
|
continue;
|
|
55090
55227
|
}
|
|
55091
55228
|
if (strikeAgeMs >= departure.confirmMinGapMs) {
|
|
@@ -55106,6 +55243,7 @@ function partitionDetectionsAgainstRegistry(input) {
|
|
|
55106
55243
|
reason: "held"
|
|
55107
55244
|
});
|
|
55108
55245
|
suppressed.add(bestIdx);
|
|
55246
|
+
continuable.add(bestIdx);
|
|
55109
55247
|
}
|
|
55110
55248
|
const wokenIds = woken.length === 0 ? EMPTY_IDS : new Set(woken.map((w) => w.entryId));
|
|
55111
55249
|
let reachSuppressedCount = 0;
|
|
@@ -55116,14 +55254,17 @@ function partitionDetectionsAgainstRegistry(input) {
|
|
|
55116
55254
|
for (let ei = 0; ei < entries.length; ei++) {
|
|
55117
55255
|
const entry = entries[ei];
|
|
55118
55256
|
if (entryClasses[ei] !== detClass || wokenIds.has(entry.id)) continue;
|
|
55119
|
-
|
|
55257
|
+
const held = parkedEntryHoldsDetection(entry, det.bbox, config);
|
|
55258
|
+
if (!held.holds) continue;
|
|
55120
55259
|
suppressed.add(di);
|
|
55260
|
+
if (!held.strong) continuable.add(di);
|
|
55121
55261
|
reachSuppressedCount += 1;
|
|
55122
55262
|
break;
|
|
55123
55263
|
}
|
|
55124
55264
|
}
|
|
55125
55265
|
return {
|
|
55126
55266
|
suppressedIndices: suppressed,
|
|
55267
|
+
continuableIndices: continuable,
|
|
55127
55268
|
confirmed,
|
|
55128
55269
|
woken,
|
|
55129
55270
|
strikeChanges,
|
|
@@ -56747,6 +56888,10 @@ var StationaryObjectRegistry = class {
|
|
|
56747
56888
|
*/
|
|
56748
56889
|
suppressedSinceSweep = /* @__PURE__ */ new Map();
|
|
56749
56890
|
reachSuppressedSinceSweep = /* @__PURE__ */ new Map();
|
|
56891
|
+
/** Weak holds handed to the tracker as continuable (D410), per device. */
|
|
56892
|
+
continuableSinceSweep = /* @__PURE__ */ new Map();
|
|
56893
|
+
/** Of those, the ones the tracker reports actually continued a track. */
|
|
56894
|
+
continuedSinceSweep = /* @__PURE__ */ new Map();
|
|
56750
56895
|
constructor(deps) {
|
|
56751
56896
|
this.logger = deps.logger;
|
|
56752
56897
|
this.ledger = new DurableLedger({
|
|
@@ -56830,6 +56975,7 @@ var StationaryObjectRegistry = class {
|
|
|
56830
56975
|
const entries = this.list(deviceId);
|
|
56831
56976
|
if (entries.length === 0) return {
|
|
56832
56977
|
suppressedIndices: /* @__PURE__ */ new Set(),
|
|
56978
|
+
continuableIndices: /* @__PURE__ */ new Set(),
|
|
56833
56979
|
confirmed: [],
|
|
56834
56980
|
woken: [],
|
|
56835
56981
|
reachSuppressedCount: 0
|
|
@@ -56850,14 +56996,23 @@ var StationaryObjectRegistry = class {
|
|
|
56850
56996
|
if (result.suppressedIndices.size > 0) {
|
|
56851
56997
|
this.suppressedSinceSweep.set(deviceId, (this.suppressedSinceSweep.get(deviceId) ?? 0) + result.suppressedIndices.size);
|
|
56852
56998
|
if (result.reachSuppressedCount > 0) this.reachSuppressedSinceSweep.set(deviceId, (this.reachSuppressedSinceSweep.get(deviceId) ?? 0) + result.reachSuppressedCount);
|
|
56999
|
+
if (result.continuableIndices.size > 0) this.continuableSinceSweep.set(deviceId, (this.continuableSinceSweep.get(deviceId) ?? 0) + result.continuableIndices.size);
|
|
56853
57000
|
}
|
|
56854
57001
|
return {
|
|
56855
57002
|
suppressedIndices: result.suppressedIndices,
|
|
57003
|
+
continuableIndices: result.continuableIndices,
|
|
56856
57004
|
confirmed: result.confirmed,
|
|
56857
57005
|
woken: result.woken,
|
|
56858
57006
|
reachSuppressedCount: result.reachSuppressedCount
|
|
56859
57007
|
};
|
|
56860
57008
|
}
|
|
57009
|
+
/** The tracker's answer to {@link StationaryFrameFilterResult.continuableIndices}:
|
|
57010
|
+
* how many of this frame's weak holds continued a confirmed track (D410).
|
|
57011
|
+
* Counted here so the sweep line can report it beside `continuable`. */
|
|
57012
|
+
noteContinued(deviceId, count) {
|
|
57013
|
+
if (count <= 0) return;
|
|
57014
|
+
this.continuedSinceSweep.set(deviceId, (this.continuedSinceSweep.get(deviceId) ?? 0) + count);
|
|
57015
|
+
}
|
|
56861
57016
|
/** Pending departure strikes for a device — the read-only diagnostic's whole
|
|
56862
57017
|
* reason to exist. Ordered oldest first so "what has been waiting" reads
|
|
56863
57018
|
* straight off the top. */
|
|
@@ -56996,6 +57151,8 @@ var StationaryObjectRegistry = class {
|
|
|
56996
57151
|
absorbed,
|
|
56997
57152
|
suppressed,
|
|
56998
57153
|
reach: this.reachSuppressedSinceSweep.get(deviceId) ?? 0,
|
|
57154
|
+
continuable: this.continuableSinceSweep.get(deviceId) ?? 0,
|
|
57155
|
+
continued: this.continuedSinceSweep.get(deviceId) ?? 0,
|
|
56999
57156
|
entries: this.count(deviceId)
|
|
57000
57157
|
}
|
|
57001
57158
|
});
|
|
@@ -57003,6 +57160,8 @@ var StationaryObjectRegistry = class {
|
|
|
57003
57160
|
this.absorbedSinceSweep.clear();
|
|
57004
57161
|
this.suppressedSinceSweep.clear();
|
|
57005
57162
|
this.reachSuppressedSinceSweep.clear();
|
|
57163
|
+
this.continuableSinceSweep.clear();
|
|
57164
|
+
this.continuedSinceSweep.clear();
|
|
57006
57165
|
for (const [deviceId, counters] of this.strikeCountersByDevice) this.logger.info("stationary departure strikes", {
|
|
57007
57166
|
tags: { deviceId },
|
|
57008
57167
|
meta: {
|
|
@@ -70652,6 +70811,22 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
70652
70811
|
* a ≤640 RAM fallback (`tier: 'ram-fullframe'`). Null until init completes. */
|
|
70653
70812
|
getNativeKeyFrameRgb = null;
|
|
70654
70813
|
shuttingDown = false;
|
|
70814
|
+
/**
|
|
70815
|
+
* Per-device delivery-lag windows, folded at `handleInferenceResult` entry.
|
|
70816
|
+
* The instrument that tells a late DELIVERY (starved) apart from a slow
|
|
70817
|
+
* `processFrame` (waiting) — see `frame-lag-observer.ts`. Costs one map read
|
|
70818
|
+
* and ~10 numeric ops per frame, and writes one line per device per 30 s.
|
|
70819
|
+
*/
|
|
70820
|
+
frameLag = new FrameLagObserver();
|
|
70821
|
+
/**
|
|
70822
|
+
* Stops the GC-attributing stall monitor armed in {@link onInitialize}.
|
|
70823
|
+
*
|
|
70824
|
+
* The other half of the frame-lag line: that one says whether the events
|
|
70825
|
+
* arrived late, this one says whether THIS loop was blocked while they did.
|
|
70826
|
+
* The 5-minute `[mem]` heartbeat cannot see a 10 s freeze — it fired at
|
|
70827
|
+
* dt = 300020 / 300066 / 299991 ms straight through the 2026-09-09 silences.
|
|
70828
|
+
*/
|
|
70829
|
+
stopStallMonitor = null;
|
|
70655
70830
|
/** True only on the cluster's designated post-processing node. When false the
|
|
70656
70831
|
* addon subscribes to NOTHING — fully inert (no event/media generation). */
|
|
70657
70832
|
isPostProcessingNode = true;
|
|
@@ -70870,6 +71045,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
70870
71045
|
super({});
|
|
70871
71046
|
}
|
|
70872
71047
|
async onInitialize() {
|
|
71048
|
+
this.stopStallMonitor = (0, _camstack_system_addon_utils.startEventLoopStallMonitor)(this.ctx.logger);
|
|
70873
71049
|
const rawApi = this.ctx.api;
|
|
70874
71050
|
if (!rawApi) throw new Error("pipeline-analytics requires ctx.api (device-manager + settings-store)");
|
|
70875
71051
|
const api = censusApi(rawApi, this.readCensus);
|
|
@@ -73374,6 +73550,10 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
73374
73550
|
}
|
|
73375
73551
|
async onShutdown() {
|
|
73376
73552
|
this.shuttingDown = true;
|
|
73553
|
+
if (this.stopStallMonitor) {
|
|
73554
|
+
this.stopStallMonitor();
|
|
73555
|
+
this.stopStallMonitor = null;
|
|
73556
|
+
}
|
|
73377
73557
|
this.sceneEngine?.stop();
|
|
73378
73558
|
this.sceneEngine = null;
|
|
73379
73559
|
if (this.sceneReloadTimer !== null) {
|
|
@@ -73418,6 +73598,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
73418
73598
|
this.overlaySynthesisWarnAt.clear();
|
|
73419
73599
|
this.processors.clear();
|
|
73420
73600
|
this.lastActiveTrackIds.clear();
|
|
73601
|
+
this.frameLag.clear();
|
|
73421
73602
|
this.lastFrameDimsByDevice.clear();
|
|
73422
73603
|
this.dropoutSkipsByKey.clear();
|
|
73423
73604
|
this.residents.clearAll();
|
|
@@ -73457,6 +73638,23 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
73457
73638
|
async handleInferenceResult(data) {
|
|
73458
73639
|
if (this.shuttingDown) return;
|
|
73459
73640
|
const { deviceId, frame } = data;
|
|
73641
|
+
const lag = this.frameLag.observe(deviceId, Date.now(), data.emittedAt, data.capturedAt);
|
|
73642
|
+
if (lag !== null) {
|
|
73643
|
+
const verdict = lagVerdict(lag);
|
|
73644
|
+
(isAlarmingLagReport(lag) ? this.ctx.logger.warn.bind(this.ctx.logger) : this.ctx.logger.info.bind(this.ctx.logger))("pipeline-analytics frame lag", {
|
|
73645
|
+
tags: { deviceId },
|
|
73646
|
+
meta: {
|
|
73647
|
+
verdict,
|
|
73648
|
+
frames: lag.frames,
|
|
73649
|
+
windowMs: lag.windowMs,
|
|
73650
|
+
busLagMaxMs: lag.busLagMaxMs,
|
|
73651
|
+
busLagMinMs: lag.busLagMinMs,
|
|
73652
|
+
busLagUnknownFrames: lag.busLagUnknownFrames,
|
|
73653
|
+
captureLagMaxMs: lag.captureLagMaxMs,
|
|
73654
|
+
captureLagUnknownFrames: lag.captureLagUnknownFrames
|
|
73655
|
+
}
|
|
73656
|
+
});
|
|
73657
|
+
}
|
|
73460
73658
|
await this.processFrame(deviceId, frame, "pipeline", data.frameHandle, data.detailSteps);
|
|
73461
73659
|
}
|
|
73462
73660
|
/**
|
|
@@ -73573,6 +73771,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
73573
73771
|
woken: result.stationaryWoken,
|
|
73574
73772
|
timestamp: result.timestamp
|
|
73575
73773
|
});
|
|
73774
|
+
if (this.stationaryRegistry && result.stationaryContinued > 0) this.stationaryRegistry.noteContinued(deviceId, result.stationaryContinued);
|
|
73576
73775
|
const stationaryViews = this.stationaryRegistry?.listViews(deviceId) ?? [];
|
|
73577
73776
|
const stationaryAsTracked = stationaryViews.map((v) => ({
|
|
73578
73777
|
trackId: `stationary:${v.id}`,
|
|
@@ -76943,6 +77142,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
76943
77142
|
const s = this.stationarySettingsFromCache(deviceId);
|
|
76944
77143
|
if (!s.enabled) return {
|
|
76945
77144
|
suppressedIndices: /* @__PURE__ */ new Set(),
|
|
77145
|
+
continuableIndices: /* @__PURE__ */ new Set(),
|
|
76946
77146
|
confirmed: [],
|
|
76947
77147
|
woken: []
|
|
76948
77148
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as VISIT_MERGE_GAP_MS, $t as object, A as NcRulePatchSchema, Bt as CamProfileSchema, C as NC_ALARM_SYSTEM_EVENT_KINDS, D as NC_TAXONOMY, Dt as readDeviceStateFrom, Et as plateGalleryCapability, F as NcSnoozeSchema, Ft as vectorDimFromBase64, G as SCENE_DEFAULT_UNCOVERED_POLICY, Gt as nodePin, H as RetrainStatusSchema, Ht as createEvent, I as NcSnoozeSuppressedSchema, It as videoclipsCapability, J as TIMELAPSE_DENSE_FLOOR_SEC, Jt as array, K as SCENE_DIVERGED, Kt as sleep$1, L as NcSystemEventKindSchema, Lt as zoneAnalyticsCapability, M as NcRuleTargetSchema, Mt as storageOccupancyCapability, N as NcScheduleSchema, Nt as subKindsOf, O as NcConditionDescriptorSchema, Ot as readTimelapseGeneratedAt, P as NcSnoozeInputSchema, Pt as systemEventFilterApplies, Q as TrackSourceSchema, Qt as number, R as NcTaxonomySchema, Rt as errMsg$1, S as MediaFileKindEnum, St as notificationRulesCapability, T as NC_DEFAULT_SNOOZE_MINUTES, Tt as pipelineAnalyticsCapability, Ut as hydrateSchema, V as RECORDING_EXPORT_MAX_READ_BYTES, Vt as DeviceType, W as SCENE_DEFAULT_ANCHOR_THRESHOLD, Wt as isDeviceScopedCap, X as TimelapseRulePatchSchema, Xt as discriminatedUnion, Y as TimelapseRuleInputSchema, Yt as boolean, Z as TimelapseRuleSchema, Zt as literal, _ as LabelAttributionSchema, _t as isDetectionMacroClass, a as CLUSTER_MODEL_SCOPED_STEPS, at as buildEventKindDescriptor, b as MAX_BIRTH_DECISION_RECORDS, bt as kebabToCamel, ct as defineCustomActions, dt as encodeVectorBase64, en as partialRecord, et as addonWidgetsSourceCapability, f as DeclaredDevices, ft as evaluateSensorEdge, g as FailureCounters, h as FULL_IMAGE_BBOX, ht as failureContributionCapability, i as BirthDecisionRecordSchema, in as EventCategory, it as audioModeOf, j as NcRuleSchema, jt as sceneMonitorCapability, k as NcRuleInputSchema, kt as resolveLocationMode, lt as deriveRecordingMode, m as EVENT_PAD_MS, mt as faceGalleryCapability, n as ArchivedDebugNoteSchema, nn as string, nt as assertTimelapseCadences, o as COCO_TO_MACRO, ot as cosineSimilarity$1, p as EVENT_KIND_BY_CAP, pt as evictionPolicyOfLocation, q as SceneMonitorSchema, qt as _enum, r as BaseDevice, rn as unknown, rt as audioMetricsCapability, s as DEFAULT_EVENT_COLOR, st as customAction, t as AUDIO_MACRO_LABELS, tn as record, tt as alarmPanelCapability, u as DETECTION_MACRO_CLASSES, v as MACRO_LABELS, vt as isScheduleActive, w as NC_CONDITION_CATALOG, wt as pickClusterStepModels, xt as mayWriteToLocation, y as MAX_ARCHIVED_DEBUG_NOTES, yt as isSourceCap, z as OpsLogEntrySchema, zt as BaseAddon } from "../dist-
|
|
1
|
+
import { $ as VISIT_MERGE_GAP_MS, $t as object, A as NcRulePatchSchema, Bt as CamProfileSchema, C as NC_ALARM_SYSTEM_EVENT_KINDS, D as NC_TAXONOMY, Dt as readDeviceStateFrom, Et as plateGalleryCapability, F as NcSnoozeSchema, Ft as vectorDimFromBase64, G as SCENE_DEFAULT_UNCOVERED_POLICY, Gt as nodePin, H as RetrainStatusSchema, Ht as createEvent, I as NcSnoozeSuppressedSchema, It as videoclipsCapability, J as TIMELAPSE_DENSE_FLOOR_SEC, Jt as array, K as SCENE_DIVERGED, Kt as sleep$1, L as NcSystemEventKindSchema, Lt as zoneAnalyticsCapability, M as NcRuleTargetSchema, Mt as storageOccupancyCapability, N as NcScheduleSchema, Nt as subKindsOf, O as NcConditionDescriptorSchema, Ot as readTimelapseGeneratedAt, P as NcSnoozeInputSchema, Pt as systemEventFilterApplies, Q as TrackSourceSchema, Qt as number, R as NcTaxonomySchema, Rt as errMsg$1, S as MediaFileKindEnum, St as notificationRulesCapability, T as NC_DEFAULT_SNOOZE_MINUTES, Tt as pipelineAnalyticsCapability, Ut as hydrateSchema, V as RECORDING_EXPORT_MAX_READ_BYTES, Vt as DeviceType, W as SCENE_DEFAULT_ANCHOR_THRESHOLD, Wt as isDeviceScopedCap, X as TimelapseRulePatchSchema, Xt as discriminatedUnion, Y as TimelapseRuleInputSchema, Yt as boolean, Z as TimelapseRuleSchema, Zt as literal, _ as LabelAttributionSchema, _t as isDetectionMacroClass, a as CLUSTER_MODEL_SCOPED_STEPS, at as buildEventKindDescriptor, b as MAX_BIRTH_DECISION_RECORDS, bt as kebabToCamel, ct as defineCustomActions, dt as encodeVectorBase64, en as partialRecord, et as addonWidgetsSourceCapability, f as DeclaredDevices, ft as evaluateSensorEdge, g as FailureCounters, h as FULL_IMAGE_BBOX, ht as failureContributionCapability, i as BirthDecisionRecordSchema, in as EventCategory, it as audioModeOf, j as NcRuleSchema, jt as sceneMonitorCapability, k as NcRuleInputSchema, kt as resolveLocationMode, lt as deriveRecordingMode, m as EVENT_PAD_MS, mt as faceGalleryCapability, n as ArchivedDebugNoteSchema, nn as string, nt as assertTimelapseCadences, o as COCO_TO_MACRO, ot as cosineSimilarity$1, p as EVENT_KIND_BY_CAP, pt as evictionPolicyOfLocation, q as SceneMonitorSchema, qt as _enum, r as BaseDevice, rn as unknown, rt as audioMetricsCapability, s as DEFAULT_EVENT_COLOR, st as customAction, t as AUDIO_MACRO_LABELS, tn as record, tt as alarmPanelCapability, u as DETECTION_MACRO_CLASSES, v as MACRO_LABELS, vt as isScheduleActive, w as NC_CONDITION_CATALOG, wt as pickClusterStepModels, xt as mayWriteToLocation, y as MAX_ARCHIVED_DEBUG_NOTES, yt as isSourceCap, z as OpsLogEntrySchema, zt as BaseAddon } from "../dist-CT2SouNJ.mjs";
|
|
2
2
|
import { t as __exportAll } from "../embedding-encoder/index.mjs";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import { promises } from "node:fs";
|
|
@@ -6,6 +6,7 @@ import * as path$1 from "node:path";
|
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { createHash, createHmac, randomUUID, timingSafeEqual } from "node:crypto";
|
|
8
8
|
import { execFile } from "node:child_process";
|
|
9
|
+
import { startEventLoopStallMonitor } from "@camstack/system/addon-utils";
|
|
9
10
|
import sharp from "sharp";
|
|
10
11
|
import os from "node:os";
|
|
11
12
|
import { Buffer as Buffer$1 } from "node:buffer";
|
|
@@ -34836,6 +34837,9 @@ var DEFAULT_TRACKER_CONFIG = {
|
|
|
34836
34837
|
var PERSON_CLASS = "person";
|
|
34837
34838
|
var ANIMAL_CLASS = "animal";
|
|
34838
34839
|
var MAX_PATH_LENGTH = 300;
|
|
34840
|
+
/** No stationary-held detections on this frame — the default for every caller
|
|
34841
|
+
* that has no gate (replay, tests, the non-pipeline planes). */
|
|
34842
|
+
var EMPTY_INDEX_SET = /* @__PURE__ */ new Set();
|
|
34839
34843
|
function clamp$1(value, min, max) {
|
|
34840
34844
|
return Math.max(min, Math.min(max, value));
|
|
34841
34845
|
}
|
|
@@ -35455,13 +35459,15 @@ var SortTracker = class SortTracker {
|
|
|
35455
35459
|
* gates) so the two-tier ByteTrack association stays a single implementation.
|
|
35456
35460
|
* Mutates `used` / `matchedTracks` / `matched` in place.
|
|
35457
35461
|
*/
|
|
35458
|
-
greedyAssociate(detections, candidateIdxs, iouThreshold, timestamp, used, matchedTracks, matched) {
|
|
35462
|
+
greedyAssociate(detections, candidateIdxs, iouThreshold, timestamp, used, matchedTracks, matched, continuationOnly) {
|
|
35459
35463
|
const pairs = [];
|
|
35460
35464
|
for (const track of this.tracks) {
|
|
35461
35465
|
if (matchedTracks.has(track)) continue;
|
|
35462
35466
|
const pbox = this.predicted(track, timestamp);
|
|
35467
|
+
const confirmed = this.isConfirmedForEmit(track);
|
|
35463
35468
|
for (const di of candidateIdxs) {
|
|
35464
35469
|
if (used.has(di)) continue;
|
|
35470
|
+
if (!confirmed && continuationOnly.has(di)) continue;
|
|
35465
35471
|
const det = detections[di];
|
|
35466
35472
|
if (!this.associable(track.class, det)) continue;
|
|
35467
35473
|
const score = iou$3(pbox, det.bbox);
|
|
@@ -35482,6 +35488,7 @@ var SortTracker = class SortTracker {
|
|
|
35482
35488
|
}
|
|
35483
35489
|
update(detections, timestamp, frameContext) {
|
|
35484
35490
|
const frameArea = frameContext ? frameContext.frameWidth * frameContext.frameHeight : 0;
|
|
35491
|
+
const continuationOnly = frameContext?.continuationOnly ?? EMPTY_INDEX_SET;
|
|
35485
35492
|
const resurrectedScene = /* @__PURE__ */ new Map();
|
|
35486
35493
|
if (this.config.maxTrackLifetimeMs > 0) {
|
|
35487
35494
|
const alive = [];
|
|
@@ -35518,12 +35525,12 @@ var SortTracker = class SortTracker {
|
|
|
35518
35525
|
for (let di = 0; di < detections.length; di++) if (this.config.byteTrackEnabled && detections[di].score < this.config.byteTrackHighThreshold) lowIdxs.push(di);
|
|
35519
35526
|
else highIdxs.push(di);
|
|
35520
35527
|
const lowSet = new Set(lowIdxs);
|
|
35521
|
-
this.greedyAssociate(detections, highIdxs, this.config.iouThreshold, timestamp, used, matchedTracks, matched);
|
|
35528
|
+
this.greedyAssociate(detections, highIdxs, this.config.iouThreshold, timestamp, used, matchedTracks, matched, continuationOnly);
|
|
35522
35529
|
const rescuePairs = [];
|
|
35523
35530
|
for (const track of this.tracks) {
|
|
35524
35531
|
if (matchedTracks.has(track)) continue;
|
|
35525
35532
|
for (let di = 0; di < detections.length; di++) {
|
|
35526
|
-
if (used.has(di) || lowSet.has(di)) continue;
|
|
35533
|
+
if (used.has(di) || lowSet.has(di) || continuationOnly.has(di)) continue;
|
|
35527
35534
|
const det = detections[di];
|
|
35528
35535
|
const gate = this.looseMatch(track, det, timestamp);
|
|
35529
35536
|
if (gate === null) continue;
|
|
@@ -35545,7 +35552,7 @@ var SortTracker = class SortTracker {
|
|
|
35545
35552
|
used.add(pair.detIdx);
|
|
35546
35553
|
this.reportBind("rescue", pair.gate, pair.track, det, timestamp);
|
|
35547
35554
|
}
|
|
35548
|
-
if (this.config.byteTrackEnabled && lowIdxs.length > 0) this.greedyAssociate(detections, lowIdxs, this.config.byteTrackLowIouThreshold, timestamp, used, matchedTracks, matched);
|
|
35555
|
+
if (this.config.byteTrackEnabled && lowIdxs.length > 0) this.greedyAssociate(detections, lowIdxs, this.config.byteTrackLowIouThreshold, timestamp, used, matchedTracks, matched, continuationOnly);
|
|
35549
35556
|
for (const [track, det] of matched) {
|
|
35550
35557
|
if (track.class !== det.class) this.reportRefusal("cross-class-absorbed", det, { against: track });
|
|
35551
35558
|
const prevCenter = bboxCentroid({
|
|
@@ -35601,7 +35608,7 @@ var SortTracker = class SortTracker {
|
|
|
35601
35608
|
} else surviving.push(track);
|
|
35602
35609
|
}
|
|
35603
35610
|
for (let di = 0; di < detections.length; di++) {
|
|
35604
|
-
if (used.has(di)) continue;
|
|
35611
|
+
if (used.has(di) || continuationOnly.has(di)) continue;
|
|
35605
35612
|
const det = detections[di];
|
|
35606
35613
|
let best;
|
|
35607
35614
|
let bestScore = -1;
|
|
@@ -35669,6 +35676,11 @@ var SortTracker = class SortTracker {
|
|
|
35669
35676
|
});
|
|
35670
35677
|
for (const di of unmatchedIdx) {
|
|
35671
35678
|
const det = detections[di];
|
|
35679
|
+
if (continuationOnly.has(di)) {
|
|
35680
|
+
this.reportRefusal("stationary-hold", det);
|
|
35681
|
+
used.add(di);
|
|
35682
|
+
continue;
|
|
35683
|
+
}
|
|
35672
35684
|
if (!this.meetsClassScoreFloor(det)) {
|
|
35673
35685
|
this.reportRefusal("score-floor", det, {
|
|
35674
35686
|
threshold: this.config.classMinScores[det.class] ?? 0,
|
|
@@ -36407,14 +36419,28 @@ var FrameProcessor = class {
|
|
|
36407
36419
|
timestamp
|
|
36408
36420
|
}) : {
|
|
36409
36421
|
suppressedIndices: /* @__PURE__ */ new Set(),
|
|
36422
|
+
continuableIndices: /* @__PURE__ */ new Set(),
|
|
36410
36423
|
confirmed: [],
|
|
36411
36424
|
woken: []
|
|
36412
36425
|
};
|
|
36413
|
-
const trackerInput =
|
|
36426
|
+
const trackerInput = [];
|
|
36427
|
+
const continuationOnly = /* @__PURE__ */ new Set();
|
|
36428
|
+
const continuableBoxes = /* @__PURE__ */ new Set();
|
|
36429
|
+
for (let i = 0; i < filteredDetections.length; i++) {
|
|
36430
|
+
const det = filteredDetections[i];
|
|
36431
|
+
if (gate.suppressedIndices.has(i)) {
|
|
36432
|
+
if (!gate.continuableIndices.has(i)) continue;
|
|
36433
|
+
continuationOnly.add(trackerInput.length);
|
|
36434
|
+
continuableBoxes.add(det.bbox);
|
|
36435
|
+
}
|
|
36436
|
+
trackerInput.push(det);
|
|
36437
|
+
}
|
|
36414
36438
|
const trackedDetections = this.tracker.update(trackerInput, timestamp, {
|
|
36415
36439
|
frameWidth,
|
|
36416
|
-
frameHeight
|
|
36440
|
+
frameHeight,
|
|
36441
|
+
continuationOnly
|
|
36417
36442
|
});
|
|
36443
|
+
const stationaryContinued = continuableBoxes.size === 0 ? 0 : trackedDetections.filter((t) => t.matchedThisFrame !== false && continuableBoxes.has(t.bbox)).length;
|
|
36418
36444
|
const riderFolds = foldByVehicleBbox.size === 0 ? EMPTY_RIDER_FOLDS : [...foldByVehicleBbox].map(([bbox, fold]) => {
|
|
36419
36445
|
const td = trackedDetections.find((t) => t.bbox === bbox && t.matchedThisFrame !== false);
|
|
36420
36446
|
return td !== void 0 ? {
|
|
@@ -36618,6 +36644,7 @@ var FrameProcessor = class {
|
|
|
36618
36644
|
rawTrackedDetections: trackedDetections,
|
|
36619
36645
|
stationaryConfirmed: gate.confirmed,
|
|
36620
36646
|
stationaryWoken: gate.woken,
|
|
36647
|
+
stationaryContinued,
|
|
36621
36648
|
riderFolds
|
|
36622
36649
|
};
|
|
36623
36650
|
}
|
|
@@ -49460,6 +49487,87 @@ var FaceRecognizer = class {
|
|
|
49460
49487
|
}
|
|
49461
49488
|
}
|
|
49462
49489
|
};
|
|
49490
|
+
/** Read the window. Pure — this is the judgement the module exists to make. */
|
|
49491
|
+
function lagVerdict(report) {
|
|
49492
|
+
const bus = report.busLagMaxMs;
|
|
49493
|
+
if (bus === null) return "unknown";
|
|
49494
|
+
return bus >= 5e3 ? "starved" : "delivered-on-time";
|
|
49495
|
+
}
|
|
49496
|
+
/** Does this window need an operator's eyes? Unknown never counts as alarming
|
|
49497
|
+
* — that is the fabricated verdict D8 forbids. */
|
|
49498
|
+
function isAlarmingLagReport(report) {
|
|
49499
|
+
const bus = report.busLagMaxMs;
|
|
49500
|
+
const capture = report.captureLagMaxMs;
|
|
49501
|
+
return bus !== null && bus >= 5e3 || capture !== null && capture >= 5e3;
|
|
49502
|
+
}
|
|
49503
|
+
/**
|
|
49504
|
+
* Folds per-frame delivery lag into per-device windows.
|
|
49505
|
+
*
|
|
49506
|
+
* Not a logger: it returns the line to write (or null) and the caller decides
|
|
49507
|
+
* level and tags. That keeps the throttle — the part that can wreck the frame
|
|
49508
|
+
* path — testable without a logger fake.
|
|
49509
|
+
*/
|
|
49510
|
+
var FrameLagObserver = class {
|
|
49511
|
+
windows = /* @__PURE__ */ new Map();
|
|
49512
|
+
/**
|
|
49513
|
+
* Fold one inference result into `deviceId`'s window.
|
|
49514
|
+
*
|
|
49515
|
+
* @param now Receipt time, read by the caller once at handler entry.
|
|
49516
|
+
* @param emittedAt Runner-side emit stamp; undefined ⇒ unknown (D8).
|
|
49517
|
+
* @param capturedAt Frame shm-commit stamp; undefined ⇒ unknown.
|
|
49518
|
+
* @returns The closed window when this frame ends one, else null.
|
|
49519
|
+
*/
|
|
49520
|
+
observe(deviceId, now, emittedAt, capturedAt) {
|
|
49521
|
+
const existing = this.windows.get(deviceId);
|
|
49522
|
+
const w = existing ?? {
|
|
49523
|
+
startedAt: now,
|
|
49524
|
+
frames: 0,
|
|
49525
|
+
busMax: null,
|
|
49526
|
+
busMin: null,
|
|
49527
|
+
busUnknown: 0,
|
|
49528
|
+
captureMax: null,
|
|
49529
|
+
captureUnknown: 0
|
|
49530
|
+
};
|
|
49531
|
+
if (existing === void 0) this.windows.set(deviceId, w);
|
|
49532
|
+
w.frames += 1;
|
|
49533
|
+
if (typeof emittedAt === "number") {
|
|
49534
|
+
const busLag = now - emittedAt;
|
|
49535
|
+
if (w.busMax === null || busLag > w.busMax) w.busMax = busLag;
|
|
49536
|
+
if (w.busMin === null || busLag < w.busMin) w.busMin = busLag;
|
|
49537
|
+
} else w.busUnknown += 1;
|
|
49538
|
+
if (typeof capturedAt === "number") {
|
|
49539
|
+
const captureLag = now - capturedAt;
|
|
49540
|
+
if (w.captureMax === null || captureLag > w.captureMax) w.captureMax = captureLag;
|
|
49541
|
+
} else w.captureUnknown += 1;
|
|
49542
|
+
if (existing !== void 0 && now - w.startedAt < 3e4) return null;
|
|
49543
|
+
const report = {
|
|
49544
|
+
deviceId,
|
|
49545
|
+
frames: w.frames,
|
|
49546
|
+
busLagMaxMs: w.busMax,
|
|
49547
|
+
busLagMinMs: w.busMin,
|
|
49548
|
+
busLagUnknownFrames: w.busUnknown,
|
|
49549
|
+
captureLagMaxMs: w.captureMax,
|
|
49550
|
+
captureLagUnknownFrames: w.captureUnknown,
|
|
49551
|
+
windowMs: now - w.startedAt
|
|
49552
|
+
};
|
|
49553
|
+
w.startedAt = now;
|
|
49554
|
+
w.frames = 0;
|
|
49555
|
+
w.busMax = null;
|
|
49556
|
+
w.busMin = null;
|
|
49557
|
+
w.busUnknown = 0;
|
|
49558
|
+
w.captureMax = null;
|
|
49559
|
+
w.captureUnknown = 0;
|
|
49560
|
+
return report;
|
|
49561
|
+
}
|
|
49562
|
+
/** Drop a device's window (unbound camera, shutdown). */
|
|
49563
|
+
forget(deviceId) {
|
|
49564
|
+
this.windows.delete(deviceId);
|
|
49565
|
+
}
|
|
49566
|
+
/** Drop every window. */
|
|
49567
|
+
clear() {
|
|
49568
|
+
this.windows.clear();
|
|
49569
|
+
}
|
|
49570
|
+
};
|
|
49463
49571
|
//#endregion
|
|
49464
49572
|
//#region src/pipeline-analytics/location-aware-media-storage.ts
|
|
49465
49573
|
/**
|
|
@@ -54915,6 +55023,31 @@ function thumbnailWouldShowParkedNotSubject(input) {
|
|
|
54915
55023
|
* dropped, against every entry that did NOT wake this frame. Woken entries are
|
|
54916
55024
|
* excluded so a departure still releases its object — a car that drives away is
|
|
54917
55025
|
* noticed on the frame its second read lands, exactly as before.
|
|
55026
|
+
*
|
|
55027
|
+
* ## A weak hold may CONTINUE a track; only a strong read is the object (D410)
|
|
55028
|
+
*
|
|
55029
|
+
* D275 predicted that a moving vehicle overlapping a parked one "loses a frame
|
|
55030
|
+
* or two while it passes" and coasts through on `maxAge`. Measured on device
|
|
55031
|
+
* 617 on 2026-09-08 it lost the whole pass: the parked cars sit at the frame
|
|
55032
|
+
* edge, so a car driving across their front is held on every frame from the
|
|
55033
|
+
* first overlap to the moment it leaves the frame, and the tracker — which had
|
|
55034
|
+
* followed it for 5–10 frames — coasts `maxMissedMs` on nothing and drops it.
|
|
55035
|
+
* Four operator-marked tracks, 1.7–3.5 s old, died on exactly that geometry;
|
|
55036
|
+
* the sweep for the window reported `reach: 0`, so the hold was pass 1's: the
|
|
55037
|
+
* mover occludes the parked car, the detector reports one vehicle box, and it
|
|
55038
|
+
* is the entry's best match.
|
|
55039
|
+
*
|
|
55040
|
+
* The hold is right — that box covers the parked spot and must not OPEN a
|
|
55041
|
+
* track, which is the whole of the operator's requirement — but it is not the
|
|
55042
|
+
* parked car: a strong read (IoU ≥ `suppressIou`) is the parked object unmoved,
|
|
55043
|
+
* and everything below it is a box that merely overlaps the spot (swallow,
|
|
55044
|
+
* jitter, departing). So `suppressedIndices` still names every box that may
|
|
55045
|
+
* not spawn, and `continuableIndices` names the weak subset the tracker may use
|
|
55046
|
+
* to CONTINUE a track it has already confirmed — through its strict
|
|
55047
|
+
* predicted-box association only, never a rescue or a resurrection. The reach
|
|
55048
|
+
* pass reports strength the same way. Nothing about confirmation, anchoring or
|
|
55049
|
+
* departure changes: a weak hold confirms and never re-anchors, exactly as
|
|
55050
|
+
* before.
|
|
54918
55051
|
*/
|
|
54919
55052
|
function partitionDetectionsAgainstRegistry(input) {
|
|
54920
55053
|
const { entries, detections, config, now } = input;
|
|
@@ -54922,6 +55055,7 @@ function partitionDetectionsAgainstRegistry(input) {
|
|
|
54922
55055
|
const departure = input.departure ?? DEFAULT_DEPARTURE_CONFIG;
|
|
54923
55056
|
const warmUp = input.warmUp ?? false;
|
|
54924
55057
|
const suppressed = /* @__PURE__ */ new Set();
|
|
55058
|
+
const continuable = /* @__PURE__ */ new Set();
|
|
54925
55059
|
const confirmed = [];
|
|
54926
55060
|
const woken = [];
|
|
54927
55061
|
const strikeChanges = [];
|
|
@@ -54961,6 +55095,7 @@ function partitionDetectionsAgainstRegistry(input) {
|
|
|
54961
55095
|
}
|
|
54962
55096
|
if (holdIdx >= 0) {
|
|
54963
55097
|
suppressed.add(holdIdx);
|
|
55098
|
+
if (!strong) continuable.add(holdIdx);
|
|
54964
55099
|
confirmed.push({
|
|
54965
55100
|
entryId: entry.id,
|
|
54966
55101
|
className: entry.className,
|
|
@@ -55000,6 +55135,7 @@ function partitionDetectionsAgainstRegistry(input) {
|
|
|
55000
55135
|
if (warmUp) {
|
|
55001
55136
|
warmUpDepartureReads += 1;
|
|
55002
55137
|
suppressed.add(bestIdx);
|
|
55138
|
+
continuable.add(bestIdx);
|
|
55003
55139
|
continue;
|
|
55004
55140
|
}
|
|
55005
55141
|
if (strike === void 0) {
|
|
@@ -55012,6 +55148,7 @@ function partitionDetectionsAgainstRegistry(input) {
|
|
|
55012
55148
|
reason: "armed"
|
|
55013
55149
|
});
|
|
55014
55150
|
suppressed.add(bestIdx);
|
|
55151
|
+
continuable.add(bestIdx);
|
|
55015
55152
|
continue;
|
|
55016
55153
|
}
|
|
55017
55154
|
if (strikeAgeMs >= departure.confirmMinGapMs) {
|
|
@@ -55032,6 +55169,7 @@ function partitionDetectionsAgainstRegistry(input) {
|
|
|
55032
55169
|
reason: "held"
|
|
55033
55170
|
});
|
|
55034
55171
|
suppressed.add(bestIdx);
|
|
55172
|
+
continuable.add(bestIdx);
|
|
55035
55173
|
}
|
|
55036
55174
|
const wokenIds = woken.length === 0 ? EMPTY_IDS : new Set(woken.map((w) => w.entryId));
|
|
55037
55175
|
let reachSuppressedCount = 0;
|
|
@@ -55042,14 +55180,17 @@ function partitionDetectionsAgainstRegistry(input) {
|
|
|
55042
55180
|
for (let ei = 0; ei < entries.length; ei++) {
|
|
55043
55181
|
const entry = entries[ei];
|
|
55044
55182
|
if (entryClasses[ei] !== detClass || wokenIds.has(entry.id)) continue;
|
|
55045
|
-
|
|
55183
|
+
const held = parkedEntryHoldsDetection(entry, det.bbox, config);
|
|
55184
|
+
if (!held.holds) continue;
|
|
55046
55185
|
suppressed.add(di);
|
|
55186
|
+
if (!held.strong) continuable.add(di);
|
|
55047
55187
|
reachSuppressedCount += 1;
|
|
55048
55188
|
break;
|
|
55049
55189
|
}
|
|
55050
55190
|
}
|
|
55051
55191
|
return {
|
|
55052
55192
|
suppressedIndices: suppressed,
|
|
55193
|
+
continuableIndices: continuable,
|
|
55053
55194
|
confirmed,
|
|
55054
55195
|
woken,
|
|
55055
55196
|
strikeChanges,
|
|
@@ -56673,6 +56814,10 @@ var StationaryObjectRegistry = class {
|
|
|
56673
56814
|
*/
|
|
56674
56815
|
suppressedSinceSweep = /* @__PURE__ */ new Map();
|
|
56675
56816
|
reachSuppressedSinceSweep = /* @__PURE__ */ new Map();
|
|
56817
|
+
/** Weak holds handed to the tracker as continuable (D410), per device. */
|
|
56818
|
+
continuableSinceSweep = /* @__PURE__ */ new Map();
|
|
56819
|
+
/** Of those, the ones the tracker reports actually continued a track. */
|
|
56820
|
+
continuedSinceSweep = /* @__PURE__ */ new Map();
|
|
56676
56821
|
constructor(deps) {
|
|
56677
56822
|
this.logger = deps.logger;
|
|
56678
56823
|
this.ledger = new DurableLedger({
|
|
@@ -56756,6 +56901,7 @@ var StationaryObjectRegistry = class {
|
|
|
56756
56901
|
const entries = this.list(deviceId);
|
|
56757
56902
|
if (entries.length === 0) return {
|
|
56758
56903
|
suppressedIndices: /* @__PURE__ */ new Set(),
|
|
56904
|
+
continuableIndices: /* @__PURE__ */ new Set(),
|
|
56759
56905
|
confirmed: [],
|
|
56760
56906
|
woken: [],
|
|
56761
56907
|
reachSuppressedCount: 0
|
|
@@ -56776,14 +56922,23 @@ var StationaryObjectRegistry = class {
|
|
|
56776
56922
|
if (result.suppressedIndices.size > 0) {
|
|
56777
56923
|
this.suppressedSinceSweep.set(deviceId, (this.suppressedSinceSweep.get(deviceId) ?? 0) + result.suppressedIndices.size);
|
|
56778
56924
|
if (result.reachSuppressedCount > 0) this.reachSuppressedSinceSweep.set(deviceId, (this.reachSuppressedSinceSweep.get(deviceId) ?? 0) + result.reachSuppressedCount);
|
|
56925
|
+
if (result.continuableIndices.size > 0) this.continuableSinceSweep.set(deviceId, (this.continuableSinceSweep.get(deviceId) ?? 0) + result.continuableIndices.size);
|
|
56779
56926
|
}
|
|
56780
56927
|
return {
|
|
56781
56928
|
suppressedIndices: result.suppressedIndices,
|
|
56929
|
+
continuableIndices: result.continuableIndices,
|
|
56782
56930
|
confirmed: result.confirmed,
|
|
56783
56931
|
woken: result.woken,
|
|
56784
56932
|
reachSuppressedCount: result.reachSuppressedCount
|
|
56785
56933
|
};
|
|
56786
56934
|
}
|
|
56935
|
+
/** The tracker's answer to {@link StationaryFrameFilterResult.continuableIndices}:
|
|
56936
|
+
* how many of this frame's weak holds continued a confirmed track (D410).
|
|
56937
|
+
* Counted here so the sweep line can report it beside `continuable`. */
|
|
56938
|
+
noteContinued(deviceId, count) {
|
|
56939
|
+
if (count <= 0) return;
|
|
56940
|
+
this.continuedSinceSweep.set(deviceId, (this.continuedSinceSweep.get(deviceId) ?? 0) + count);
|
|
56941
|
+
}
|
|
56787
56942
|
/** Pending departure strikes for a device — the read-only diagnostic's whole
|
|
56788
56943
|
* reason to exist. Ordered oldest first so "what has been waiting" reads
|
|
56789
56944
|
* straight off the top. */
|
|
@@ -56922,6 +57077,8 @@ var StationaryObjectRegistry = class {
|
|
|
56922
57077
|
absorbed,
|
|
56923
57078
|
suppressed,
|
|
56924
57079
|
reach: this.reachSuppressedSinceSweep.get(deviceId) ?? 0,
|
|
57080
|
+
continuable: this.continuableSinceSweep.get(deviceId) ?? 0,
|
|
57081
|
+
continued: this.continuedSinceSweep.get(deviceId) ?? 0,
|
|
56925
57082
|
entries: this.count(deviceId)
|
|
56926
57083
|
}
|
|
56927
57084
|
});
|
|
@@ -56929,6 +57086,8 @@ var StationaryObjectRegistry = class {
|
|
|
56929
57086
|
this.absorbedSinceSweep.clear();
|
|
56930
57087
|
this.suppressedSinceSweep.clear();
|
|
56931
57088
|
this.reachSuppressedSinceSweep.clear();
|
|
57089
|
+
this.continuableSinceSweep.clear();
|
|
57090
|
+
this.continuedSinceSweep.clear();
|
|
56932
57091
|
for (const [deviceId, counters] of this.strikeCountersByDevice) this.logger.info("stationary departure strikes", {
|
|
56933
57092
|
tags: { deviceId },
|
|
56934
57093
|
meta: {
|
|
@@ -70578,6 +70737,22 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
70578
70737
|
* a ≤640 RAM fallback (`tier: 'ram-fullframe'`). Null until init completes. */
|
|
70579
70738
|
getNativeKeyFrameRgb = null;
|
|
70580
70739
|
shuttingDown = false;
|
|
70740
|
+
/**
|
|
70741
|
+
* Per-device delivery-lag windows, folded at `handleInferenceResult` entry.
|
|
70742
|
+
* The instrument that tells a late DELIVERY (starved) apart from a slow
|
|
70743
|
+
* `processFrame` (waiting) — see `frame-lag-observer.ts`. Costs one map read
|
|
70744
|
+
* and ~10 numeric ops per frame, and writes one line per device per 30 s.
|
|
70745
|
+
*/
|
|
70746
|
+
frameLag = new FrameLagObserver();
|
|
70747
|
+
/**
|
|
70748
|
+
* Stops the GC-attributing stall monitor armed in {@link onInitialize}.
|
|
70749
|
+
*
|
|
70750
|
+
* The other half of the frame-lag line: that one says whether the events
|
|
70751
|
+
* arrived late, this one says whether THIS loop was blocked while they did.
|
|
70752
|
+
* The 5-minute `[mem]` heartbeat cannot see a 10 s freeze — it fired at
|
|
70753
|
+
* dt = 300020 / 300066 / 299991 ms straight through the 2026-09-09 silences.
|
|
70754
|
+
*/
|
|
70755
|
+
stopStallMonitor = null;
|
|
70581
70756
|
/** True only on the cluster's designated post-processing node. When false the
|
|
70582
70757
|
* addon subscribes to NOTHING — fully inert (no event/media generation). */
|
|
70583
70758
|
isPostProcessingNode = true;
|
|
@@ -70796,6 +70971,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
70796
70971
|
super({});
|
|
70797
70972
|
}
|
|
70798
70973
|
async onInitialize() {
|
|
70974
|
+
this.stopStallMonitor = startEventLoopStallMonitor(this.ctx.logger);
|
|
70799
70975
|
const rawApi = this.ctx.api;
|
|
70800
70976
|
if (!rawApi) throw new Error("pipeline-analytics requires ctx.api (device-manager + settings-store)");
|
|
70801
70977
|
const api = censusApi(rawApi, this.readCensus);
|
|
@@ -73300,6 +73476,10 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
73300
73476
|
}
|
|
73301
73477
|
async onShutdown() {
|
|
73302
73478
|
this.shuttingDown = true;
|
|
73479
|
+
if (this.stopStallMonitor) {
|
|
73480
|
+
this.stopStallMonitor();
|
|
73481
|
+
this.stopStallMonitor = null;
|
|
73482
|
+
}
|
|
73303
73483
|
this.sceneEngine?.stop();
|
|
73304
73484
|
this.sceneEngine = null;
|
|
73305
73485
|
if (this.sceneReloadTimer !== null) {
|
|
@@ -73344,6 +73524,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
73344
73524
|
this.overlaySynthesisWarnAt.clear();
|
|
73345
73525
|
this.processors.clear();
|
|
73346
73526
|
this.lastActiveTrackIds.clear();
|
|
73527
|
+
this.frameLag.clear();
|
|
73347
73528
|
this.lastFrameDimsByDevice.clear();
|
|
73348
73529
|
this.dropoutSkipsByKey.clear();
|
|
73349
73530
|
this.residents.clearAll();
|
|
@@ -73383,6 +73564,23 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
73383
73564
|
async handleInferenceResult(data) {
|
|
73384
73565
|
if (this.shuttingDown) return;
|
|
73385
73566
|
const { deviceId, frame } = data;
|
|
73567
|
+
const lag = this.frameLag.observe(deviceId, Date.now(), data.emittedAt, data.capturedAt);
|
|
73568
|
+
if (lag !== null) {
|
|
73569
|
+
const verdict = lagVerdict(lag);
|
|
73570
|
+
(isAlarmingLagReport(lag) ? this.ctx.logger.warn.bind(this.ctx.logger) : this.ctx.logger.info.bind(this.ctx.logger))("pipeline-analytics frame lag", {
|
|
73571
|
+
tags: { deviceId },
|
|
73572
|
+
meta: {
|
|
73573
|
+
verdict,
|
|
73574
|
+
frames: lag.frames,
|
|
73575
|
+
windowMs: lag.windowMs,
|
|
73576
|
+
busLagMaxMs: lag.busLagMaxMs,
|
|
73577
|
+
busLagMinMs: lag.busLagMinMs,
|
|
73578
|
+
busLagUnknownFrames: lag.busLagUnknownFrames,
|
|
73579
|
+
captureLagMaxMs: lag.captureLagMaxMs,
|
|
73580
|
+
captureLagUnknownFrames: lag.captureLagUnknownFrames
|
|
73581
|
+
}
|
|
73582
|
+
});
|
|
73583
|
+
}
|
|
73386
73584
|
await this.processFrame(deviceId, frame, "pipeline", data.frameHandle, data.detailSteps);
|
|
73387
73585
|
}
|
|
73388
73586
|
/**
|
|
@@ -73499,6 +73697,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
73499
73697
|
woken: result.stationaryWoken,
|
|
73500
73698
|
timestamp: result.timestamp
|
|
73501
73699
|
});
|
|
73700
|
+
if (this.stationaryRegistry && result.stationaryContinued > 0) this.stationaryRegistry.noteContinued(deviceId, result.stationaryContinued);
|
|
73502
73701
|
const stationaryViews = this.stationaryRegistry?.listViews(deviceId) ?? [];
|
|
73503
73702
|
const stationaryAsTracked = stationaryViews.map((v) => ({
|
|
73504
73703
|
trackId: `stationary:${v.id}`,
|
|
@@ -76869,6 +77068,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
76869
77068
|
const s = this.stationarySettingsFromCache(deviceId);
|
|
76870
77069
|
if (!s.enabled) return {
|
|
76871
77070
|
suppressedIndices: /* @__PURE__ */ new Set(),
|
|
77071
|
+
continuableIndices: /* @__PURE__ */ new Set(),
|
|
76872
77072
|
confirmed: [],
|
|
76873
77073
|
woken: []
|
|
76874
77074
|
};
|
|
@@ -30,7 +30,7 @@ async function d(e) {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
async function f() {
|
|
33
|
-
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-
|
|
33
|
+
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-TUlCd-Zk.mjs")).catch((e) => {
|
|
34
34
|
throw l = void 0, e;
|
|
35
35
|
}), l;
|
|
36
36
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-post-analysis",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.214",
|
|
4
4
|
"description": "Post-Analysis bundle — enrichment, embedding-encoder, pipeline-analytics. Multi-entry npm package shipping addons that consume pipeline output.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|