@camstack/addon-post-analysis 1.2.663 → 1.2.665
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/CHANGELOG.md +14 -0
- package/dist/{clip-model-registry-CwXJyrLg.mjs → clip-model-registry-DZTzG2p4.mjs} +377 -132
- package/dist/{clip-model-registry-Cz8LWYwU.js → clip-model-registry-nn_STiGt.js} +382 -131
- package/dist/embedding-encoder/index.js +1 -1
- package/dist/embedding-encoder/index.mjs +1 -1
- package/dist/pipeline-analytics/_stub.js +2 -2
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-jPBj20ez.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-Hy74pMMo.mjs} +2 -2
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BXTAZYaJ.mjs +26 -0
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-Br6eWzp_.mjs +26 -0
- package/dist/pipeline-analytics/{hostInit-DOdK6KJ5.mjs → hostInit-BAYLsfoZ.mjs} +2 -2
- package/dist/pipeline-analytics/index.js +438 -61
- package/dist/pipeline-analytics/index.mjs +438 -61
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +2 -2
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Be6bZvOS.mjs +0 -26
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-B5Bw-VHt.mjs +0 -26
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_clip_model_registry = require("../clip-model-registry-
|
|
5
|
+
const require_clip_model_registry = require("../clip-model-registry-nn_STiGt.js");
|
|
6
6
|
let node_fs = require("node:fs");
|
|
7
7
|
node_fs = require_clip_model_registry.__toESM(node_fs, 1);
|
|
8
8
|
let node_path = require("node:path");
|
|
@@ -12284,6 +12284,16 @@ var NcStateSummaryTestSchema = require_clip_model_registry.object({
|
|
|
12284
12284
|
}))
|
|
12285
12285
|
});
|
|
12286
12286
|
//#endregion
|
|
12287
|
+
//#region src/notification-center/template-defaults.ts
|
|
12288
|
+
function servedTemplateDefaults(texts) {
|
|
12289
|
+
return [{
|
|
12290
|
+
id: "appliance-cycle",
|
|
12291
|
+
kind: "sensor",
|
|
12292
|
+
title: texts.raw("appliance.finished.title") ?? "",
|
|
12293
|
+
body: texts.raw("appliance.finished.body") ?? ""
|
|
12294
|
+
}];
|
|
12295
|
+
}
|
|
12296
|
+
//#endregion
|
|
12287
12297
|
//#region src/notification-center/clock-text.ts
|
|
12288
12298
|
/**
|
|
12289
12299
|
* `{{time}}` — hours and minutes in the NOTIFICATION language.
|
|
@@ -13682,7 +13692,10 @@ var ncActions = require_clip_model_registry.defineCustomActions({
|
|
|
13682
13692
|
catalog: require_clip_model_registry.array(require_clip_model_registry.NcConditionDescriptorSchema),
|
|
13683
13693
|
taxonomy: require_clip_model_registry.NcTaxonomySchema
|
|
13684
13694
|
})),
|
|
13685
|
-
"nc.getTemplateCatalog": require_clip_model_registry.customAction(require_clip_model_registry.object({}), require_clip_model_registry.object({
|
|
13695
|
+
"nc.getTemplateCatalog": require_clip_model_registry.customAction(require_clip_model_registry.object({}), require_clip_model_registry.object({
|
|
13696
|
+
vars: require_clip_model_registry.array(require_clip_model_registry.NcTemplateVarDescriptorSchema),
|
|
13697
|
+
defaults: require_clip_model_registry.array(require_clip_model_registry.NcTemplateDefaultSchema)
|
|
13698
|
+
})),
|
|
13686
13699
|
"nc.previewTemplate": require_clip_model_registry.customAction(require_clip_model_registry.NcTemplatePreviewInputSchema, require_clip_model_registry.NcTemplatePreviewSchema, { kind: "mutation" }),
|
|
13687
13700
|
"nc.createRule": require_clip_model_registry.customAction(require_clip_model_registry.object({ rule: require_clip_model_registry.NcRuleInputSchema }), require_clip_model_registry.object({ rule: require_clip_model_registry.NcRuleSchema }), {
|
|
13688
13701
|
kind: "mutation",
|
|
@@ -14462,7 +14475,10 @@ function makeNcActionHandlers(deps) {
|
|
|
14462
14475
|
catalog: [...require_clip_model_registry.NC_CONDITION_CATALOG],
|
|
14463
14476
|
taxonomy: require_clip_model_registry.NC_TAXONOMY
|
|
14464
14477
|
}),
|
|
14465
|
-
"nc.getTemplateCatalog": async () => ({
|
|
14478
|
+
"nc.getTemplateCatalog": async () => ({
|
|
14479
|
+
vars: require_clip_model_registry.servedTemplateVars(),
|
|
14480
|
+
defaults: deps.previewTexts === void 0 ? [] : servedTemplateDefaults(deps.previewTexts)
|
|
14481
|
+
}),
|
|
14466
14482
|
"nc.previewTemplate": async (input) => {
|
|
14467
14483
|
const texts = deps.previewTexts;
|
|
14468
14484
|
if (texts === void 0) throw new Error("nc.previewTemplate: text catalog not wired");
|
|
@@ -49951,7 +49967,7 @@ async function encodeRgbCropToJpeg(bytes, width, height) {
|
|
|
49951
49967
|
* what lets the caller stop base64-ing purely to be understood.
|
|
49952
49968
|
*/
|
|
49953
49969
|
async function decodeJpegToRgb(jpeg) {
|
|
49954
|
-
const { data, info } = await (0, sharp.default)(typeof jpeg === "string" ? Buffer.from(jpeg, "base64") : jpeg).raw().toBuffer({ resolveWithObject: true });
|
|
49970
|
+
const { data, info } = await (0, sharp.default)(typeof jpeg === "string" ? Buffer.from(jpeg, "base64") : jpeg).removeAlpha().toColourspace("srgb").raw().toBuffer({ resolveWithObject: true });
|
|
49955
49971
|
return {
|
|
49956
49972
|
bytes: data,
|
|
49957
49973
|
width: info.width,
|
|
@@ -53037,6 +53053,42 @@ function collectPackageZoneIds(rules) {
|
|
|
53037
53053
|
}
|
|
53038
53054
|
return ids;
|
|
53039
53055
|
}
|
|
53056
|
+
/**
|
|
53057
|
+
* How much the package zone box grows on every side when asking whether motion
|
|
53058
|
+
* reached it. Mirrors the D144 dilation of the pipeline's burst trigger
|
|
53059
|
+
* (`PACKAGE_ZONE_DILATION` in addon-pipeline, not importable from here): about
|
|
53060
|
+
* one adult's width beside a doormat, the place a courier stands.
|
|
53061
|
+
*/
|
|
53062
|
+
var PACKAGE_ZONE_MOTION_DILATION = .5;
|
|
53063
|
+
/**
|
|
53064
|
+
* A motion region covering more than this fraction of the frame is a light
|
|
53065
|
+
* change or the weather, not a person: it never counts as a deliverer.
|
|
53066
|
+
*/
|
|
53067
|
+
var PACKAGE_ZONE_MOTION_MAX_REGION_FRAC = .8;
|
|
53068
|
+
/**
|
|
53069
|
+
* Did analyzer motion reach the dilated package zone with a person-sized
|
|
53070
|
+
* region? Unknown geometry answers false: this stamp only ever BACKS the open
|
|
53071
|
+
* of a candidate, so failing closed keeps the light-wedge guard intact.
|
|
53072
|
+
*/
|
|
53073
|
+
function delivererMotionNearPackageZone(input) {
|
|
53074
|
+
const { zoneRect, frameWidth, frameHeight } = input;
|
|
53075
|
+
if (zoneRect === null || frameWidth <= 0 || frameHeight <= 0) return false;
|
|
53076
|
+
const dx = zoneRect.w * PACKAGE_ZONE_MOTION_DILATION;
|
|
53077
|
+
const dy = zoneRect.h * PACKAGE_ZONE_MOTION_DILATION;
|
|
53078
|
+
const left = Math.max(0, zoneRect.x - dx);
|
|
53079
|
+
const top = Math.max(0, zoneRect.y - dy);
|
|
53080
|
+
const right = Math.min(1, zoneRect.x + zoneRect.w + dx);
|
|
53081
|
+
const bottom = Math.min(1, zoneRect.y + zoneRect.h + dy);
|
|
53082
|
+
for (const region of input.regions) {
|
|
53083
|
+
const x = region.bbox.x / frameWidth;
|
|
53084
|
+
const y = region.bbox.y / frameHeight;
|
|
53085
|
+
const w = region.bbox.w / frameWidth;
|
|
53086
|
+
const h = region.bbox.h / frameHeight;
|
|
53087
|
+
if (w * h > PACKAGE_ZONE_MOTION_MAX_REGION_FRAC) continue;
|
|
53088
|
+
if (x < right && left < x + w && y < bottom && top < y + h) return true;
|
|
53089
|
+
}
|
|
53090
|
+
return false;
|
|
53091
|
+
}
|
|
53040
53092
|
//#endregion
|
|
53041
53093
|
//#region src/pipeline-analytics/pipeline/package/package-collector-mirror.ts
|
|
53042
53094
|
/**
|
|
@@ -53982,6 +54034,15 @@ var PACKAGE_STILLNESS_TTL_MS = 5 * 6e4;
|
|
|
53982
54034
|
*/
|
|
53983
54035
|
var PACKAGE_STILLNESS_ASSOC_IOU = .3;
|
|
53984
54036
|
/**
|
|
54037
|
+
* Lowest score of a below-threshold box that may CONTINUE an open candidate
|
|
54038
|
+
* (D665: a box under the floor may continue, never open). Observed on a real
|
|
54039
|
+
* parcel after the day to IR flip: 0.35 to 0.50.
|
|
54040
|
+
*/
|
|
54041
|
+
var PACKAGE_STILLNESS_CONTINUATION_MIN_SCORE = .3;
|
|
54042
|
+
/** Above-floor sightings a candidate needs before a sub-floor box may count:
|
|
54043
|
+
* one confident look plus a doormat-shaped reading is not a parcel. */
|
|
54044
|
+
var PACKAGE_CONTINUATION_MIN_REAL_HITS = 2;
|
|
54045
|
+
/**
|
|
53985
54046
|
* Two absence reads closer than this are ONE look, not two. A settle burst
|
|
53986
54047
|
* runs the package step 5 s apart and a poll 45 s apart, so a real second
|
|
53987
54048
|
* look is always further away; two frames of one moment (a re-dispatch, a
|
|
@@ -54031,6 +54092,7 @@ var PackageStillnessTracker = class {
|
|
|
54031
54092
|
const next = [...this.byDevice.get(input.deviceId) ?? []];
|
|
54032
54093
|
const consumed = /* @__PURE__ */ new Set();
|
|
54033
54094
|
const promoted = [];
|
|
54095
|
+
const continuedLog = [];
|
|
54034
54096
|
for (const s of input.sightings) {
|
|
54035
54097
|
const index = this.bestMatch(next, s, consumed);
|
|
54036
54098
|
if (index < 0) {
|
|
@@ -54043,17 +54105,33 @@ var PackageStillnessTracker = class {
|
|
|
54043
54105
|
if (prev === void 0) continue;
|
|
54044
54106
|
consumed.add(prev.id);
|
|
54045
54107
|
const updated = confirm(prev, input, s);
|
|
54046
|
-
const
|
|
54047
|
-
|
|
54048
|
-
|
|
54049
|
-
|
|
54050
|
-
|
|
54051
|
-
const
|
|
54052
|
-
|
|
54053
|
-
|
|
54108
|
+
const parked = this.promoteWhenReady(updated, input);
|
|
54109
|
+
next[index] = parked ?? updated;
|
|
54110
|
+
if (parked !== null) promoted.push(toEntry(parked));
|
|
54111
|
+
}
|
|
54112
|
+
if (input.looked === true && input.continuationBoxes !== void 0) for (let i = 0; i < next.length; i += 1) {
|
|
54113
|
+
const c = next[i];
|
|
54114
|
+
if (c === void 0 || c.promotedAt !== null || consumed.has(c.id)) continue;
|
|
54115
|
+
if (c.realHits < PACKAGE_CONTINUATION_MIN_REAL_HITS) continue;
|
|
54116
|
+
const box = this.continuationFor(c, input.continuationBoxes);
|
|
54117
|
+
if (box === null) continue;
|
|
54118
|
+
consumed.add(c.id);
|
|
54119
|
+
const continued = {
|
|
54120
|
+
...c,
|
|
54121
|
+
lastSeenAt: input.timestamp,
|
|
54122
|
+
hits: c.hits + 1,
|
|
54123
|
+
absenceReads: [],
|
|
54124
|
+
lastLook: "present",
|
|
54125
|
+
earlyUnbackedReported: false
|
|
54054
54126
|
};
|
|
54055
|
-
|
|
54056
|
-
|
|
54127
|
+
continuedLog.push({
|
|
54128
|
+
id: c.id,
|
|
54129
|
+
hits: continued.hits,
|
|
54130
|
+
score: box.score
|
|
54131
|
+
});
|
|
54132
|
+
const parked = this.promoteWhenReady(continued, input);
|
|
54133
|
+
next[i] = parked ?? continued;
|
|
54134
|
+
if (parked !== null) promoted.push(toEntry(parked));
|
|
54057
54135
|
}
|
|
54058
54136
|
const present = input.rawPackageBoxes !== void 0 ? withRawPresence(next, consumed, input.rawPackageBoxes, input.timestamp, this.config.assocIou) : next;
|
|
54059
54137
|
const looked = input.looked === true && input.rawPackageBoxes !== void 0 ? withLook(present, consumed, input, input.rawPackageBoxes, this.config.assocIou) : present;
|
|
@@ -54089,7 +54167,8 @@ var PackageStillnessTracker = class {
|
|
|
54089
54167
|
...input.lastActivityAt !== void 0 ? { lastActivityAt: input.lastActivityAt } : {}
|
|
54090
54168
|
}),
|
|
54091
54169
|
earlyDepartureIds: [...early],
|
|
54092
|
-
earlyEvidenceUnbacked: unbacked
|
|
54170
|
+
earlyEvidenceUnbacked: unbacked,
|
|
54171
|
+
continued: continuedLog
|
|
54093
54172
|
};
|
|
54094
54173
|
}
|
|
54095
54174
|
/** Why complete early evidence may NOT conclude, or null when it may. */
|
|
@@ -54116,7 +54195,8 @@ var PackageStillnessTracker = class {
|
|
|
54116
54195
|
...input.lastActivityAt !== void 0 ? { lastActivityAt: input.lastActivityAt } : {}
|
|
54117
54196
|
}),
|
|
54118
54197
|
earlyDepartureIds: [],
|
|
54119
|
-
earlyEvidenceUnbacked: []
|
|
54198
|
+
earlyEvidenceUnbacked: [],
|
|
54199
|
+
continued: []
|
|
54120
54200
|
};
|
|
54121
54201
|
}
|
|
54122
54202
|
/** The devices holding a candidate — what a sweep has to visit. */
|
|
@@ -54180,6 +54260,43 @@ var PackageStillnessTracker = class {
|
|
|
54180
54260
|
/** Best same-class candidate over the association floor, ignoring any already
|
|
54181
54261
|
* consumed by an earlier sighting in THIS frame (two boxes never collapse
|
|
54182
54262
|
* into one parcel). */
|
|
54263
|
+
continuationFor(c, boxes) {
|
|
54264
|
+
let best = null;
|
|
54265
|
+
let bestIou = this.config.assocIou;
|
|
54266
|
+
for (const b of boxes) {
|
|
54267
|
+
if (b.score < PACKAGE_STILLNESS_CONTINUATION_MIN_SCORE) continue;
|
|
54268
|
+
const overlap = iou$2(c.bbox, b.bbox);
|
|
54269
|
+
if (overlap >= bestIou) {
|
|
54270
|
+
bestIou = overlap;
|
|
54271
|
+
best = b;
|
|
54272
|
+
}
|
|
54273
|
+
}
|
|
54274
|
+
return best;
|
|
54275
|
+
}
|
|
54276
|
+
promoteWhenReady(candidate, input) {
|
|
54277
|
+
const updated = this.backedLate(candidate, input);
|
|
54278
|
+
const minHits = input.minHits ?? this.config.minHits;
|
|
54279
|
+
return updated.promotedAt === null && updated.activityBackedAtOpen && updated.hits >= minHits && input.timestamp - updated.firstSeenAt >= input.minDwellMs ? {
|
|
54280
|
+
...updated,
|
|
54281
|
+
promotedAt: input.timestamp
|
|
54282
|
+
} : null;
|
|
54283
|
+
}
|
|
54284
|
+
/**
|
|
54285
|
+
* Zone motion learned AFTER the open still backs the candidate when its stamp
|
|
54286
|
+
* lies in the window around the FIRST sighting (the same slack a pick-up
|
|
54287
|
+
* gets): the backing stays anchored at the open, a later passer-by never
|
|
54288
|
+
* validates scenery (D1003).
|
|
54289
|
+
*/
|
|
54290
|
+
backedLate(c, input) {
|
|
54291
|
+
if (c.activityBackedAtOpen || c.promotedAt !== null) return c;
|
|
54292
|
+
const at = input.lastDelivererMotionAt;
|
|
54293
|
+
const windowMs = input.activityWindowMs ?? 0;
|
|
54294
|
+
if (at === void 0 || windowMs <= 0) return c;
|
|
54295
|
+
return at >= c.firstSeenAt - windowMs && at <= c.firstSeenAt + this.config.roundGapMs ? {
|
|
54296
|
+
...c,
|
|
54297
|
+
activityBackedAtOpen: true
|
|
54298
|
+
} : c;
|
|
54299
|
+
}
|
|
54183
54300
|
bestMatch(candidates, s, consumed) {
|
|
54184
54301
|
let bestIndex = -1;
|
|
54185
54302
|
let bestIou = this.config.assocIou;
|
|
@@ -54196,7 +54313,7 @@ var PackageStillnessTracker = class {
|
|
|
54196
54313
|
}
|
|
54197
54314
|
openCandidate(input, s) {
|
|
54198
54315
|
const windowMs = input.activityWindowMs ?? 0;
|
|
54199
|
-
const activityBackedAtOpen = windowMs <= 0 || input.lastActivityAt !== void 0 && input.timestamp - input.lastActivityAt <= windowMs;
|
|
54316
|
+
const activityBackedAtOpen = windowMs <= 0 || input.lastActivityAt !== void 0 && input.timestamp - input.lastActivityAt <= windowMs || input.lastDelivererMotionAt !== void 0 && input.timestamp - input.lastDelivererMotionAt <= windowMs;
|
|
54200
54317
|
return {
|
|
54201
54318
|
id: this.newId(),
|
|
54202
54319
|
deviceId: input.deviceId,
|
|
@@ -54207,6 +54324,7 @@ var PackageStillnessTracker = class {
|
|
|
54207
54324
|
firstSeenAt: input.timestamp,
|
|
54208
54325
|
lastSeenAt: input.timestamp,
|
|
54209
54326
|
hits: 1,
|
|
54327
|
+
realHits: 1,
|
|
54210
54328
|
promotedAt: null,
|
|
54211
54329
|
absenceReads: [],
|
|
54212
54330
|
lastLook: null,
|
|
@@ -54229,6 +54347,7 @@ function confirm(prev, input, s) {
|
|
|
54229
54347
|
frameHeight: input.frameHeight,
|
|
54230
54348
|
lastSeenAt: input.timestamp,
|
|
54231
54349
|
hits: prev.hits + 1,
|
|
54350
|
+
realHits: prev.realHits + 1,
|
|
54232
54351
|
absenceReads: [],
|
|
54233
54352
|
lastLook: "present",
|
|
54234
54353
|
earlyUnbackedReported: false,
|
|
@@ -54765,6 +54884,9 @@ var FrameState = class {
|
|
|
54765
54884
|
* which would let the streetlight wedge vouch for its own delivery.
|
|
54766
54885
|
*/
|
|
54767
54886
|
lastNonPackageActivityAt = /* @__PURE__ */ new Map();
|
|
54887
|
+
/** Last analyzer motion near a package drop-off zone, per device (D1003).
|
|
54888
|
+
* Backs the OPEN of a candidate only; never a pick-up witness. */
|
|
54889
|
+
lastPackageZoneMotionAt = /* @__PURE__ */ new Map();
|
|
54768
54890
|
/**
|
|
54769
54891
|
* WHO was in the drop-off zone, and when (D404). The pick-up is CONCLUDED
|
|
54770
54892
|
* minutes after the fact, so the collector must be remembered while it is
|
|
@@ -58124,6 +58246,7 @@ function nextPackageLookDropEpisodes(previous, drops) {
|
|
|
58124
58246
|
//#region src/pipeline-analytics/pipeline/package/package-look-evidence.ts
|
|
58125
58247
|
function packageLookEvidence(input) {
|
|
58126
58248
|
const raw = [];
|
|
58249
|
+
const continuation = [];
|
|
58127
58250
|
for (const d of input.frame.detections) {
|
|
58128
58251
|
if (d.kind !== "first-level" || !input.packageClasses.has(d.macroClass)) continue;
|
|
58129
58252
|
raw.push({
|
|
@@ -58136,11 +58259,16 @@ function packageLookEvidence(input) {
|
|
|
58136
58259
|
for (const d of input.frame.discarded ?? []) {
|
|
58137
58260
|
if (!input.packageClasses.has(d.macroClass)) continue;
|
|
58138
58261
|
const [x1, y1, x2, y2] = d.bbox;
|
|
58139
|
-
|
|
58262
|
+
const box = {
|
|
58140
58263
|
x: x1,
|
|
58141
58264
|
y: y1,
|
|
58142
58265
|
w: x2 - x1,
|
|
58143
58266
|
h: y2 - y1
|
|
58267
|
+
};
|
|
58268
|
+
raw.push(box);
|
|
58269
|
+
if (d.reason === "below-threshold") continuation.push({
|
|
58270
|
+
bbox: box,
|
|
58271
|
+
score: d.score
|
|
58144
58272
|
});
|
|
58145
58273
|
}
|
|
58146
58274
|
return {
|
|
@@ -58152,6 +58280,7 @@ function packageLookEvidence(input) {
|
|
|
58152
58280
|
h: t.bbox.h
|
|
58153
58281
|
})),
|
|
58154
58282
|
rawPackageBoxes: raw,
|
|
58283
|
+
continuationBoxes: continuation,
|
|
58155
58284
|
earlyPickup: input.earlyPickupEnabled
|
|
58156
58285
|
};
|
|
58157
58286
|
}
|
|
@@ -58339,6 +58468,13 @@ function clamp01(value) {
|
|
|
58339
58468
|
}
|
|
58340
58469
|
//#endregion
|
|
58341
58470
|
//#region src/pipeline-analytics/pipeline/package/package-scene-diff.ts
|
|
58471
|
+
/**
|
|
58472
|
+
* Zone-crop scene difference for a packages rule (D931).
|
|
58473
|
+
*
|
|
58474
|
+
* A uniform brightness shift is removed by subtracting the median luma delta,
|
|
58475
|
+
* so a shadow that lifts the whole crop leaves no blob. What remains is a
|
|
58476
|
+
* connected region. The package model is not consulted here.
|
|
58477
|
+
*/
|
|
58342
58478
|
/** Residual, after the median shift, that counts as a changed pixel. */
|
|
58343
58479
|
var PACKAGE_SCENE_DIFF_LUMA_THRESHOLD = 28;
|
|
58344
58480
|
/** A changed region this large, relative to the zone, is the zone itself. */
|
|
@@ -58353,12 +58489,26 @@ var PACKAGE_SCENE_DIFF_SETTLE_MS = 3e3;
|
|
|
58353
58489
|
var PACKAGE_SCENE_DIFF_LOOK_GAP_MS = 2e3;
|
|
58354
58490
|
/** Confidence stamped on a scene-diff sighting the model did not box. */
|
|
58355
58491
|
var PACKAGE_SCENE_DIFF_CONFIDENCE = .5;
|
|
58492
|
+
/** True when the crop was shot in infrared (no colour). */
|
|
58493
|
+
function isIrCrop(crop) {
|
|
58494
|
+
return crop.chromaSpread < IR_CHROMA_THRESHOLD;
|
|
58495
|
+
}
|
|
58356
58496
|
function diffZoneLuma(input) {
|
|
58357
58497
|
const { before, after } = input;
|
|
58358
58498
|
if (before.width !== after.width || before.height !== after.height || before.luma.length !== after.luma.length) return {
|
|
58359
58499
|
blobs: [],
|
|
58360
58500
|
rejections: [{ reason: "crop-mismatch" }]
|
|
58361
58501
|
};
|
|
58502
|
+
if (isIrCrop(before) !== isIrCrop(after)) return {
|
|
58503
|
+
blobs: [],
|
|
58504
|
+
rejections: [{
|
|
58505
|
+
reason: "lighting-mode-changed",
|
|
58506
|
+
chroma: {
|
|
58507
|
+
before: before.chromaSpread,
|
|
58508
|
+
after: after.chromaSpread
|
|
58509
|
+
}
|
|
58510
|
+
}]
|
|
58511
|
+
};
|
|
58362
58512
|
const median = medianDelta(before.luma, after.luma);
|
|
58363
58513
|
const mask = residualMask(before.luma, after.luma, median);
|
|
58364
58514
|
const zonePx = before.width * before.height;
|
|
@@ -58573,6 +58723,7 @@ function okCrop(rgb, width, height, jpeg) {
|
|
|
58573
58723
|
width,
|
|
58574
58724
|
height,
|
|
58575
58725
|
luma: lumaFromRgb(rgb, width, height),
|
|
58726
|
+
chromaSpread: chromaSpreadFromRgb(rgb, width, height),
|
|
58576
58727
|
jpeg: new Uint8Array(jpeg)
|
|
58577
58728
|
};
|
|
58578
58729
|
}
|
|
@@ -58589,6 +58740,46 @@ function lumaFromRgb(bytes, width, height) {
|
|
|
58589
58740
|
}
|
|
58590
58741
|
return luma;
|
|
58591
58742
|
}
|
|
58743
|
+
/** The grid `IR_CHROMA_THRESHOLD` was calibrated on (`chromaSpread`, scene-crop.ts). */
|
|
58744
|
+
var CHROMA_GRID = 32;
|
|
58745
|
+
/**
|
|
58746
|
+
* Mean of max(r,g,b) - min(r,g,b), 0..255, measured on a 32x32 box-averaged
|
|
58747
|
+
* copy of the crop: the same measure and grid as `chromaSpread` in
|
|
58748
|
+
* `scene-crop.ts` (which needs sharp), so `IR_CHROMA_THRESHOLD` means the same
|
|
58749
|
+
* thing. Averaging first matters: sensor noise and JPEG chroma speckle on an IR
|
|
58750
|
+
* frame cancel out, where per-pixel they read as colour.
|
|
58751
|
+
*/
|
|
58752
|
+
function chromaSpreadFromRgb(bytes, width, height) {
|
|
58753
|
+
if (width <= 0 || height <= 0 || bytes.length < width * height * 3) return 0;
|
|
58754
|
+
const gw = Math.min(CHROMA_GRID, width);
|
|
58755
|
+
const gh = Math.min(CHROMA_GRID, height);
|
|
58756
|
+
const sum = new Float64Array(gw * gh * 3);
|
|
58757
|
+
const count = new Float64Array(gw * gh);
|
|
58758
|
+
for (let y = 0; y < height; y++) {
|
|
58759
|
+
const cy = Math.min(gh - 1, Math.floor(y * gh / height));
|
|
58760
|
+
for (let x = 0; x < width; x++) {
|
|
58761
|
+
const cx = Math.min(gw - 1, Math.floor(x * gw / width));
|
|
58762
|
+
const cell = cy * gw + cx;
|
|
58763
|
+
const o = (y * width + x) * 3;
|
|
58764
|
+
sum[cell * 3] = (sum[cell * 3] ?? 0) + (bytes[o] ?? 0);
|
|
58765
|
+
sum[cell * 3 + 1] = (sum[cell * 3 + 1] ?? 0) + (bytes[o + 1] ?? 0);
|
|
58766
|
+
sum[cell * 3 + 2] = (sum[cell * 3 + 2] ?? 0) + (bytes[o + 2] ?? 0);
|
|
58767
|
+
count[cell] = (count[cell] ?? 0) + 1;
|
|
58768
|
+
}
|
|
58769
|
+
}
|
|
58770
|
+
let total = 0;
|
|
58771
|
+
let cells = 0;
|
|
58772
|
+
for (let cell = 0; cell < gw * gh; cell++) {
|
|
58773
|
+
const n = count[cell] ?? 0;
|
|
58774
|
+
if (n === 0) continue;
|
|
58775
|
+
const r = (sum[cell * 3] ?? 0) / n;
|
|
58776
|
+
const g = (sum[cell * 3 + 1] ?? 0) / n;
|
|
58777
|
+
const b = (sum[cell * 3 + 2] ?? 0) / n;
|
|
58778
|
+
total += Math.max(r, g, b) - Math.min(r, g, b);
|
|
58779
|
+
cells += 1;
|
|
58780
|
+
}
|
|
58781
|
+
return cells === 0 ? 0 : total / cells;
|
|
58782
|
+
}
|
|
58592
58783
|
//#endregion
|
|
58593
58784
|
//#region src/pipeline-analytics/pipeline/package/package-scene-step.ts
|
|
58594
58785
|
/**
|
|
@@ -58688,6 +58879,14 @@ function compareAfterLeave(prev, input) {
|
|
|
58688
58879
|
minAreaFrac: input.minAreaFrac,
|
|
58689
58880
|
occluders: input.occluders
|
|
58690
58881
|
});
|
|
58882
|
+
if (diff.rejections.some((r) => r.reason === "lighting-mode-changed")) return {
|
|
58883
|
+
state: {
|
|
58884
|
+
...rememberClear(prev, input).state,
|
|
58885
|
+
lastDiffAt: input.timestamp
|
|
58886
|
+
},
|
|
58887
|
+
sightings: [],
|
|
58888
|
+
rejections: diff.rejections
|
|
58889
|
+
};
|
|
58691
58890
|
const blocked = diff.rejections.some((r) => r.reason === "overlaps-person" || r.reason === "overlaps-vehicle");
|
|
58692
58891
|
if (diff.blobs.length === 0) {
|
|
58693
58892
|
if (blocked) return {
|
|
@@ -58731,6 +58930,20 @@ function confirmPending(prev, input) {
|
|
|
58731
58930
|
sightings: [],
|
|
58732
58931
|
rejections: []
|
|
58733
58932
|
};
|
|
58933
|
+
if (isIrCrop(prev.after) !== isIrCrop(input.crop)) return {
|
|
58934
|
+
state: {
|
|
58935
|
+
...rememberClear(prev, input).state,
|
|
58936
|
+
lastDiffAt: input.timestamp
|
|
58937
|
+
},
|
|
58938
|
+
sightings: [],
|
|
58939
|
+
rejections: [{
|
|
58940
|
+
reason: "lighting-mode-changed",
|
|
58941
|
+
chroma: {
|
|
58942
|
+
before: prev.after.chromaSpread,
|
|
58943
|
+
after: input.crop.chromaSpread
|
|
58944
|
+
}
|
|
58945
|
+
}]
|
|
58946
|
+
};
|
|
58734
58947
|
const sightings = [];
|
|
58735
58948
|
const rejections = [];
|
|
58736
58949
|
const still = [];
|
|
@@ -58777,7 +58990,7 @@ function judgeBlob(prev, input, blob) {
|
|
|
58777
58990
|
bbox: blob.bbox
|
|
58778
58991
|
} };
|
|
58779
58992
|
const stable = meanAbsInBox(after, crop, blob.bbox) <= PACKAGE_SCENE_DIFF_STABLE_MEAN;
|
|
58780
|
-
const present = meanAbsInBox(before, crop, blob.bbox) >= PACKAGE_SCENE_DIFF_LUMA_THRESHOLD;
|
|
58993
|
+
const present = isIrCrop(before) !== isIrCrop(crop) || meanAbsInBox(before, crop, blob.bbox) >= PACKAGE_SCENE_DIFF_LUMA_THRESHOLD;
|
|
58781
58994
|
if (!stable || !present) return { rejection: {
|
|
58782
58995
|
reason: "unstable",
|
|
58783
58996
|
bbox: blob.bbox,
|
|
@@ -58945,7 +59158,8 @@ var PackageSceneCoordinator = class {
|
|
|
58945
59158
|
const crop = {
|
|
58946
59159
|
width: fetched.width,
|
|
58947
59160
|
height: fetched.height,
|
|
58948
|
-
luma: fetched.luma
|
|
59161
|
+
luma: fetched.luma,
|
|
59162
|
+
chromaSpread: fetched.chromaSpread
|
|
58949
59163
|
};
|
|
58950
59164
|
const stepped = stepPackageScene({
|
|
58951
59165
|
deviceId,
|
|
@@ -59011,6 +59225,10 @@ var PackageSceneCoordinator = class {
|
|
|
59011
59225
|
tags: { deviceId },
|
|
59012
59226
|
meta: {
|
|
59013
59227
|
reason: rejection.reason,
|
|
59228
|
+
...rejection.chroma !== void 0 ? {
|
|
59229
|
+
chromaBefore: Number(rejection.chroma.before.toFixed(2)),
|
|
59230
|
+
chromaAfter: Number(rejection.chroma.after.toFixed(2))
|
|
59231
|
+
} : {},
|
|
59014
59232
|
...rejection.areaFrac !== void 0 ? { areaFrac: Number(rejection.areaFrac.toFixed(5)) } : {}
|
|
59015
59233
|
}
|
|
59016
59234
|
});
|
|
@@ -59119,14 +59337,8 @@ function planPackageSceneFrame(deviceId, input) {
|
|
|
59119
59337
|
frameWidth: input.frameWidth,
|
|
59120
59338
|
frameHeight: input.frameHeight
|
|
59121
59339
|
});
|
|
59122
|
-
const sceneRect = sceneZones.size === 0 ? null : packageZoneCropRect({
|
|
59123
|
-
zones: input.zones,
|
|
59124
|
-
packageZoneIds: sceneZones,
|
|
59125
|
-
frameWidth: input.frameWidth,
|
|
59126
|
-
frameHeight: input.frameHeight
|
|
59127
|
-
});
|
|
59128
59340
|
return {
|
|
59129
|
-
sightings:
|
|
59341
|
+
sightings: input.sightings,
|
|
59130
59342
|
note: {
|
|
59131
59343
|
deviceId,
|
|
59132
59344
|
timestamp: input.timestamp,
|
|
@@ -59169,15 +59381,6 @@ function occludersOf(tracked) {
|
|
|
59169
59381
|
}
|
|
59170
59382
|
return out;
|
|
59171
59383
|
}
|
|
59172
|
-
function withoutSceneCenters(input, rect) {
|
|
59173
|
-
if (input.frameWidth <= 0 || input.frameHeight <= 0) return input.sightings;
|
|
59174
|
-
return input.sightings.filter((sighting) => !centerInside(sighting.bbox, rect, input));
|
|
59175
|
-
}
|
|
59176
|
-
function centerInside(bbox, rect, input) {
|
|
59177
|
-
const cx = (bbox.x + bbox.w / 2) / input.frameWidth;
|
|
59178
|
-
const cy = (bbox.y + bbox.h / 2) / input.frameHeight;
|
|
59179
|
-
return cx >= rect.x && cx < rect.x + rect.w && cy >= rect.y && cy < rect.y + rect.h;
|
|
59180
|
-
}
|
|
59181
59384
|
//#endregion
|
|
59182
59385
|
//#region src/pipeline-analytics/pipeline/package/package-llm-confirm.ts
|
|
59183
59386
|
/**
|
|
@@ -59338,56 +59541,143 @@ function sweepOne(deps, deviceId) {
|
|
|
59338
59541
|
}
|
|
59339
59542
|
//#endregion
|
|
59340
59543
|
//#region src/pipeline-analytics/pipeline/package/package-still-capture.ts
|
|
59341
|
-
/**
|
|
59342
|
-
|
|
59544
|
+
/**
|
|
59545
|
+
* The runner's native lease on a look frame (holdFrames 8) is released about
|
|
59546
|
+
* 100 ms after inference, so a handle older than this answers `native-miss` or
|
|
59547
|
+
* `preview-only` — promotion runs seconds to minutes after the look (D60).
|
|
59548
|
+
*/
|
|
59549
|
+
var PACKAGE_NATIVE_HANDLE_MAX_AGE_MS = 1e3;
|
|
59550
|
+
/** Total time a promotion may spend landing its still before it ships without (D60). */
|
|
59551
|
+
var PACKAGE_STILL_BOUND_MS = 6e3;
|
|
59552
|
+
/** One live-source attempt never takes longer than this (a sleeping camera). */
|
|
59553
|
+
var PACKAGE_STILL_ATTEMPT_TIMEOUT_MS = 3e3;
|
|
59554
|
+
/** Pause between two live attempts. */
|
|
59555
|
+
var PACKAGE_STILL_RETRY_PAUSE_MS = 750;
|
|
59556
|
+
var PACKAGE_STILL_LIVE_ATTEMPTS = 3;
|
|
59557
|
+
var realPause = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
59343
59558
|
async function jpegOf(reply) {
|
|
59344
59559
|
if (reply.tier === "ram-fullframe") return "preview-only";
|
|
59345
59560
|
if (reply.jpegBytes !== void 0 && reply.jpegBytes.byteLength > 0) return reply.jpegBytes;
|
|
59346
59561
|
if (reply.bytes !== void 0 && reply.bytes.length === reply.width * reply.height * 3) return encodeRgbCropToJpeg(reply.bytes, reply.width, reply.height);
|
|
59347
59562
|
return "empty-frame";
|
|
59348
59563
|
}
|
|
59564
|
+
/** One live read, cut off after `limitMs` so a hung camera cannot hold the delivery. */
|
|
59565
|
+
async function readLive(source, limitMs) {
|
|
59566
|
+
let timer;
|
|
59567
|
+
const timeout = new Promise((resolve) => {
|
|
59568
|
+
timer = setTimeout(() => resolve(null), limitMs);
|
|
59569
|
+
});
|
|
59570
|
+
try {
|
|
59571
|
+
return await Promise.race([source(), timeout]);
|
|
59572
|
+
} finally {
|
|
59573
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
59574
|
+
}
|
|
59575
|
+
}
|
|
59576
|
+
/** Retry the live source under {@link PACKAGE_STILL_BOUND_MS} until a frame lands (D60). */
|
|
59577
|
+
async function liveStill(input, source) {
|
|
59578
|
+
const now = input.now ?? Date.now;
|
|
59579
|
+
const pause = input.pause ?? realPause;
|
|
59580
|
+
const deadline = now() + PACKAGE_STILL_BOUND_MS;
|
|
59581
|
+
let reason = "live-unavailable";
|
|
59582
|
+
let attempts = 0;
|
|
59583
|
+
while (attempts < PACKAGE_STILL_LIVE_ATTEMPTS) {
|
|
59584
|
+
const remaining = deadline - now();
|
|
59585
|
+
if (remaining <= 0) return {
|
|
59586
|
+
jpeg: null,
|
|
59587
|
+
reason: "bound-exhausted",
|
|
59588
|
+
attempts
|
|
59589
|
+
};
|
|
59590
|
+
attempts += 1;
|
|
59591
|
+
try {
|
|
59592
|
+
const jpeg = await readLive(source, Math.min(PACKAGE_STILL_ATTEMPT_TIMEOUT_MS, remaining));
|
|
59593
|
+
if (jpeg !== null && jpeg.byteLength > 0) return {
|
|
59594
|
+
jpeg,
|
|
59595
|
+
reason,
|
|
59596
|
+
attempts
|
|
59597
|
+
};
|
|
59598
|
+
reason = jpeg === null ? "live-unavailable" : "empty-frame";
|
|
59599
|
+
} catch {
|
|
59600
|
+
reason = "live-unavailable";
|
|
59601
|
+
}
|
|
59602
|
+
if (attempts < PACKAGE_STILL_LIVE_ATTEMPTS) await pause(PACKAGE_STILL_RETRY_PAUSE_MS);
|
|
59603
|
+
}
|
|
59604
|
+
return {
|
|
59605
|
+
jpeg: null,
|
|
59606
|
+
reason,
|
|
59607
|
+
attempts
|
|
59608
|
+
};
|
|
59609
|
+
}
|
|
59349
59610
|
/**
|
|
59350
59611
|
* The entry carrying its still's `keyFrameMediaId`, or the entry unchanged when
|
|
59351
59612
|
* it already had one or none could be stored (said, with the reason).
|
|
59613
|
+
*
|
|
59614
|
+
* Ladder: the native handle while its lease can still be alive, then a frame
|
|
59615
|
+
* that exists now (retried under a bound). Every landed still is logged next to
|
|
59616
|
+
* every miss (D400).
|
|
59352
59617
|
*/
|
|
59353
59618
|
async function withParcelStill(input) {
|
|
59354
59619
|
const { entry, logger } = input;
|
|
59355
59620
|
if (entry.keyFrameMediaId !== void 0) return entry;
|
|
59356
|
-
const
|
|
59621
|
+
const tags = { deviceId: entry.deviceId };
|
|
59622
|
+
const miss = (reason, extra = {}) => {
|
|
59357
59623
|
logger.warn("package still NOT stored — the delivery ships without a picture of the parcel", {
|
|
59358
|
-
tags
|
|
59624
|
+
tags,
|
|
59359
59625
|
meta: {
|
|
59360
59626
|
entryId: entry.id,
|
|
59361
59627
|
reason,
|
|
59362
|
-
...
|
|
59628
|
+
...extra
|
|
59363
59629
|
}
|
|
59364
59630
|
});
|
|
59365
59631
|
return entry;
|
|
59366
59632
|
};
|
|
59367
|
-
if (input.handle === null) return miss("no-frame-handle");
|
|
59368
59633
|
if (input.writer === null) return miss("no-media-store");
|
|
59634
|
+
const now = input.now ?? Date.now;
|
|
59635
|
+
let jpeg = null;
|
|
59636
|
+
let source = "native";
|
|
59637
|
+
let stamp = input.timestamp;
|
|
59638
|
+
let nativeMiss = "no-frame-handle";
|
|
59639
|
+
let liveReason = null;
|
|
59640
|
+
let attempts = 0;
|
|
59369
59641
|
try {
|
|
59370
|
-
|
|
59371
|
-
|
|
59372
|
-
|
|
59373
|
-
|
|
59374
|
-
|
|
59375
|
-
|
|
59376
|
-
|
|
59377
|
-
|
|
59378
|
-
|
|
59379
|
-
|
|
59380
|
-
|
|
59642
|
+
if (input.handle !== null) {
|
|
59643
|
+
const reply = await input.query({
|
|
59644
|
+
handle: input.handle,
|
|
59645
|
+
bbox: {
|
|
59646
|
+
x: 0,
|
|
59647
|
+
y: 0,
|
|
59648
|
+
w: entry.frameWidth,
|
|
59649
|
+
h: entry.frameHeight
|
|
59650
|
+
},
|
|
59651
|
+
acceptJpegBytes: true,
|
|
59652
|
+
encodeJpeg: true,
|
|
59653
|
+
pin: require_clip_model_registry.nodePin(input.handle.nodeId)
|
|
59654
|
+
});
|
|
59655
|
+
if (reply === null) nativeMiss = "native-miss";
|
|
59656
|
+
else {
|
|
59657
|
+
const out = await jpegOf(reply);
|
|
59658
|
+
if (typeof out === "string") nativeMiss = out;
|
|
59659
|
+
else jpeg = out;
|
|
59660
|
+
}
|
|
59661
|
+
}
|
|
59662
|
+
if (jpeg === null && input.live !== null) {
|
|
59663
|
+
const live = await liveStill(input, input.live);
|
|
59664
|
+
attempts = live.attempts;
|
|
59665
|
+
if (live.jpeg !== null) {
|
|
59666
|
+
jpeg = live.jpeg;
|
|
59667
|
+
source = "live";
|
|
59668
|
+
stamp = now();
|
|
59669
|
+
} else liveReason = live.reason;
|
|
59670
|
+
}
|
|
59671
|
+
if (jpeg === null) return miss(liveReason ?? nativeMiss, {
|
|
59672
|
+
...input.handle !== null ? { nativeMiss } : {},
|
|
59673
|
+
...liveReason !== null ? { liveAttempts: attempts } : {}
|
|
59381
59674
|
});
|
|
59382
|
-
if (reply === null) return miss("native-miss");
|
|
59383
|
-
const jpeg = await jpegOf(reply);
|
|
59384
|
-
if (typeof jpeg === "string") return miss(jpeg);
|
|
59385
59675
|
const keyFrameMediaId = await input.writer.putReplacing({
|
|
59386
59676
|
deviceId: entry.deviceId,
|
|
59387
59677
|
ownerKind: "track",
|
|
59388
59678
|
ownerId: entry.id,
|
|
59389
59679
|
kind: "keyFrame",
|
|
59390
|
-
timestamp:
|
|
59680
|
+
timestamp: stamp,
|
|
59391
59681
|
data: Buffer.from(jpeg),
|
|
59392
59682
|
subjectBox: {
|
|
59393
59683
|
x: entry.bbox.x,
|
|
@@ -59398,12 +59688,20 @@ async function withParcelStill(input) {
|
|
|
59398
59688
|
frameHeight: entry.frameHeight
|
|
59399
59689
|
}
|
|
59400
59690
|
});
|
|
59691
|
+
logger.info("package still stored", {
|
|
59692
|
+
tags,
|
|
59693
|
+
meta: {
|
|
59694
|
+
entryId: entry.id,
|
|
59695
|
+
mediaId: keyFrameMediaId,
|
|
59696
|
+
source
|
|
59697
|
+
}
|
|
59698
|
+
});
|
|
59401
59699
|
return {
|
|
59402
59700
|
...entry,
|
|
59403
59701
|
keyFrameMediaId
|
|
59404
59702
|
};
|
|
59405
59703
|
} catch (err) {
|
|
59406
|
-
return miss("write-failed", require_clip_model_registry.errMsg(err));
|
|
59704
|
+
return miss("write-failed", { error: require_clip_model_registry.errMsg(err) });
|
|
59407
59705
|
}
|
|
59408
59706
|
}
|
|
59409
59707
|
/**
|
|
@@ -71062,6 +71360,7 @@ Object.freeze({
|
|
|
71062
71360
|
"addon-pages-source": ["listPages"],
|
|
71063
71361
|
"addon-routes": ["getRoutes"],
|
|
71064
71362
|
"addon-widgets-source": ["listWidgets"],
|
|
71363
|
+
"board-key-source": ["listSources"],
|
|
71065
71364
|
"broker": ["list", "listProviders"],
|
|
71066
71365
|
"calendar-source": ["listCalendars", "listEvents"],
|
|
71067
71366
|
"custom-model-registry": ["listModels"],
|
|
@@ -72590,7 +72889,7 @@ function buildGlobalSettingsSchema(cap) {
|
|
|
72590
72889
|
type: "slider",
|
|
72591
72890
|
key: "packageDropActivityWindowSec",
|
|
72592
72891
|
label: "Deliverer window",
|
|
72593
|
-
description: "A delivery only counts if a person or vehicle was in the configured package drop-off zone within this many seconds before the package appeared — and a pick-up only with somebody in that zone when it vanished. Motion elsewhere on the camera does not count. 0 disables the requirement.",
|
|
72892
|
+
description: "A delivery only counts if a person or vehicle was in the configured package drop-off zone within this many seconds before the package appeared — and a pick-up only with somebody in that zone when it vanished. Person-sized motion beside the drop-off zone also counts when nobody was tracked. Motion elsewhere on the camera does not count. 0 disables the requirement.",
|
|
72594
72893
|
min: 0,
|
|
72595
72894
|
max: 1800,
|
|
72596
72895
|
step: 30,
|
|
@@ -87110,6 +87409,13 @@ var NC_STILL_WAIT_MAX_AGE_MS = 3e4;
|
|
|
87110
87409
|
function canStillGainMedia(entry) {
|
|
87111
87410
|
return entry.recordKind === "object-event";
|
|
87112
87411
|
}
|
|
87412
|
+
/** The variables that carry a finished cycle's run summary. */
|
|
87413
|
+
var CYCLE_SUMMARY_VARS = [
|
|
87414
|
+
"duration",
|
|
87415
|
+
"energy",
|
|
87416
|
+
"afterDuration",
|
|
87417
|
+
"usedEnergy"
|
|
87418
|
+
];
|
|
87113
87419
|
var NcDispatcher = class {
|
|
87114
87420
|
deps;
|
|
87115
87421
|
targetCache = null;
|
|
@@ -88013,6 +88319,7 @@ var NcDispatcher = class {
|
|
|
88013
88319
|
}
|
|
88014
88320
|
/** Rules already warned about a deprecated variable in this process — once each. */
|
|
88015
88321
|
deprecatedVarWarned = /* @__PURE__ */ new Set();
|
|
88322
|
+
cycleSummaryWarned = /* @__PURE__ */ new Set();
|
|
88016
88323
|
/**
|
|
88017
88324
|
* A rule template written by an older client may still say `{{camera}}`. It
|
|
88018
88325
|
* renders (the alias), and the rule is named once so the operator can edit it.
|
|
@@ -88032,6 +88339,27 @@ var NcDispatcher = class {
|
|
|
88032
88339
|
});
|
|
88033
88340
|
}
|
|
88034
88341
|
/**
|
|
88342
|
+
* A FINISHED-cycle row whose own template places none of the four cycle
|
|
88343
|
+
* variables delivers no run summary (D391: accepted, produced less). Said
|
|
88344
|
+
* once per rule; the operator's text is never touched.
|
|
88345
|
+
*/
|
|
88346
|
+
warnCycleSummaryDropped(entry) {
|
|
88347
|
+
const deviceId = entry.payload.subject.applianceCycle?.deviceId;
|
|
88348
|
+
const template = entry.payload.template;
|
|
88349
|
+
if (deviceId === void 0 || template === void 0) return;
|
|
88350
|
+
if (this.cycleSummaryWarned.has(entry.ruleId)) return;
|
|
88351
|
+
const used = new Set([...templatePlaceholders(template.title ?? ""), ...templatePlaceholders(template.body ?? "")]);
|
|
88352
|
+
if (CYCLE_SUMMARY_VARS.some((name) => used.has(name))) return;
|
|
88353
|
+
this.cycleSummaryWarned.add(entry.ruleId);
|
|
88354
|
+
this.deps.logger.warn("appliance cycle finished but the rule template places no run summary — the message carries no duration or energy", {
|
|
88355
|
+
tags: {
|
|
88356
|
+
deviceId,
|
|
88357
|
+
ruleId: entry.ruleId
|
|
88358
|
+
},
|
|
88359
|
+
meta: { placeholders: [...CYCLE_SUMMARY_VARS] }
|
|
88360
|
+
});
|
|
88361
|
+
}
|
|
88362
|
+
/**
|
|
88035
88363
|
* The facts of a finished appliance cycle; unknown on every other row and on
|
|
88036
88364
|
* any failure (a part that cannot be read is omitted, never a blocked send).
|
|
88037
88365
|
*/
|
|
@@ -88076,6 +88404,7 @@ var NcDispatcher = class {
|
|
|
88076
88404
|
const cycleFacts = await this.applianceCycleFacts(entry);
|
|
88077
88405
|
const vars = buildTemplateVars(this.texts, entry, deviceName, zoneLabels, void 0, cycleFacts);
|
|
88078
88406
|
this.warnDeprecatedVars(entry);
|
|
88407
|
+
this.warnCycleSummaryDropped(entry);
|
|
88079
88408
|
const systemCount = systemEvent?.textCount !== void 0 ? { count: systemEvent.textCount } : {};
|
|
88080
88409
|
const title = renderRowTitle(this.texts, entry, vars);
|
|
88081
88410
|
const authoredBody = entry.payload.template?.body;
|
|
@@ -102058,7 +102387,10 @@ function buildProvider(host) {
|
|
|
102058
102387
|
catalog: [...require_clip_model_registry.NC_CONDITION_CATALOG],
|
|
102059
102388
|
taxonomy: require_clip_model_registry.NC_TAXONOMY
|
|
102060
102389
|
}),
|
|
102061
|
-
getTemplateCatalog: async () => ({
|
|
102390
|
+
getTemplateCatalog: async () => ({
|
|
102391
|
+
vars: require_clip_model_registry.servedTemplateVars(),
|
|
102392
|
+
defaults: servedTemplateDefaults(host.texts)
|
|
102393
|
+
}),
|
|
102062
102394
|
previewTemplate: async (input) => previewTemplate(host.texts, input),
|
|
102063
102395
|
getHistory: async ({ filter }) => {
|
|
102064
102396
|
const entries = await host.outbox.queryHistory({
|
|
@@ -124995,6 +125327,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
124995
125327
|
this.packageDropDetector?.forgetDevice(data.deviceId);
|
|
124996
125328
|
this.packageScene?.forget(data.deviceId);
|
|
124997
125329
|
this.frameState.lastNonPackageActivityAt.delete(data.deviceId);
|
|
125330
|
+
this.frameState.lastPackageZoneMotionAt.delete(data.deviceId);
|
|
124998
125331
|
this.frameState.packageCollectors.forgetDevice(data.deviceId);
|
|
124999
125332
|
this.frameState.packageOversizeLogAt.delete(data.deviceId);
|
|
125000
125333
|
this.frameState.packageLookFrames.delete(data.deviceId);
|
|
@@ -125019,6 +125352,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
125019
125352
|
this.packageDropDetector?.forgetDevice(deviceId);
|
|
125020
125353
|
this.packageScene?.forget(deviceId);
|
|
125021
125354
|
this.frameState.lastNonPackageActivityAt.delete(deviceId);
|
|
125355
|
+
this.frameState.lastPackageZoneMotionAt.delete(deviceId);
|
|
125022
125356
|
this.frameState.packageCollectors.forgetDevice(deviceId);
|
|
125023
125357
|
this.frameState.packageOversizeLogAt.delete(deviceId);
|
|
125024
125358
|
this.frameState.packageLookFrames.delete(deviceId);
|
|
@@ -127591,6 +127925,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
127591
127925
|
detected,
|
|
127592
127926
|
atMs: timestamp
|
|
127593
127927
|
});
|
|
127928
|
+
if (detected) await this.stampPackageZoneMotion(deviceId, payload, timestamp);
|
|
127594
127929
|
if (detected) this.eventStore?.noteMotionSample(deviceId, payload.regionCount, timestamp);
|
|
127595
127930
|
await this.motionEpisodes?.observe(deviceId, "analyzer", detected, timestamp, {
|
|
127596
127931
|
regionCount: payload.regionCount,
|
|
@@ -127609,6 +127944,28 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
127609
127944
|
});
|
|
127610
127945
|
}
|
|
127611
127946
|
/**
|
|
127947
|
+
* Stamp analyzer motion that reached a package drop-off zone (D1003): the
|
|
127948
|
+
* courier who never became an L1 track still backs the OPEN of a candidate.
|
|
127949
|
+
* Onboard motion carries no regions or dimensions and never stamps.
|
|
127950
|
+
*/
|
|
127951
|
+
async stampPackageZoneMotion(deviceId, payload, timestamp) {
|
|
127952
|
+
const proxy = await this.ensureProxy(deviceId);
|
|
127953
|
+
const packageZoneIds = collectPackageZoneIds(proxy?.state.zoneRules.value?.package ?? []);
|
|
127954
|
+
if (packageZoneIds.size === 0) return;
|
|
127955
|
+
const zoneRect = packageZoneCropRect({
|
|
127956
|
+
zones: proxy?.state.zones.value?.zones ?? [],
|
|
127957
|
+
packageZoneIds,
|
|
127958
|
+
frameWidth: payload.frameWidth,
|
|
127959
|
+
frameHeight: payload.frameHeight
|
|
127960
|
+
});
|
|
127961
|
+
if (delivererMotionNearPackageZone({
|
|
127962
|
+
regions: payload.regions,
|
|
127963
|
+
frameWidth: payload.frameWidth,
|
|
127964
|
+
frameHeight: payload.frameHeight,
|
|
127965
|
+
zoneRect
|
|
127966
|
+
})) this.frameState.lastPackageZoneMotionAt.set(deviceId, timestamp);
|
|
127967
|
+
}
|
|
127968
|
+
/**
|
|
127612
127969
|
* Persist firmware-driven (onboard / device-activity) motion events into
|
|
127613
127970
|
* the SAME episode tracker as {@link handleMotionAnalysis}, keyed per
|
|
127614
127971
|
* `(deviceId, source)` so the two paths can never share — or fight over —
|
|
@@ -127941,6 +128298,10 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
127941
128298
|
encodeJpeg: request.encodeJpeg
|
|
127942
128299
|
}, request.pin);
|
|
127943
128300
|
}
|
|
128301
|
+
zoneMotionStamp(deviceId) {
|
|
128302
|
+
const at = this.frameState.lastPackageZoneMotionAt.get(deviceId);
|
|
128303
|
+
return at === void 0 ? {} : { lastDelivererMotionAt: at };
|
|
128304
|
+
}
|
|
127944
128305
|
deliverPackageSceneSightings(delivery) {
|
|
127945
128306
|
const settings = this.packageDropSettingsFromCache(delivery.deviceId);
|
|
127946
128307
|
const lastActivityAt = this.frameState.lastNonPackageActivityAt.get(delivery.deviceId);
|
|
@@ -127954,6 +128315,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
127954
128315
|
minDwellMs: settings.packageDropDwellSec * 1e3,
|
|
127955
128316
|
activityWindowMs: settings.packageDropActivityWindowSec * 1e3,
|
|
127956
128317
|
...lastActivityAt !== void 0 ? { lastActivityAt } : {},
|
|
128318
|
+
...this.zoneMotionStamp(delivery.deviceId),
|
|
127957
128319
|
looked: false
|
|
127958
128320
|
});
|
|
127959
128321
|
this.bridgePackageStillness({
|
|
@@ -128139,6 +128501,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
128139
128501
|
minDwellMs: input.settings.packageDropDwellSec * 1e3,
|
|
128140
128502
|
activityWindowMs: input.settings.packageDropActivityWindowSec * 1e3,
|
|
128141
128503
|
...lastActivityAt !== void 0 ? { lastActivityAt } : {},
|
|
128504
|
+
...this.zoneMotionStamp(input.deviceId),
|
|
128142
128505
|
...lookEvidence
|
|
128143
128506
|
});
|
|
128144
128507
|
this.bridgePackageStillness({
|
|
@@ -128244,11 +128607,15 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
128244
128607
|
const lookFrame = this.frameState.packageLookFrames.get(input.deviceId);
|
|
128245
128608
|
promoteWithStill({
|
|
128246
128609
|
entry,
|
|
128247
|
-
handle: lookFrame !== void 0 && input.timestamp - lookFrame.at <=
|
|
128610
|
+
handle: lookFrame !== void 0 && input.timestamp - lookFrame.at <= PACKAGE_NATIVE_HANDLE_MAX_AGE_MS ? lookFrame.handle : null,
|
|
128248
128611
|
timestamp: lookFrame?.at ?? input.timestamp,
|
|
128249
128612
|
query: (request) => this.queryPackageSceneCrop(request),
|
|
128250
128613
|
writer: this.mediaStore,
|
|
128251
128614
|
logger: this.ctx.logger,
|
|
128615
|
+
live: async () => {
|
|
128616
|
+
const snap = await this.ctx.api.snapshot.getSnapshot.query({ deviceId: input.deviceId });
|
|
128617
|
+
return snap === null ? null : Uint8Array.from(Buffer.from(snap.base64, "base64"));
|
|
128618
|
+
},
|
|
128252
128619
|
detector: this.packageDropDetector
|
|
128253
128620
|
}).catch((err) => {
|
|
128254
128621
|
this.ctx.logger.warn("package promotion failed", {
|
|
@@ -128330,6 +128697,15 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
128330
128697
|
});
|
|
128331
128698
|
}).catch(() => void 0);
|
|
128332
128699
|
}
|
|
128700
|
+
for (const c of outcome.continued) this.ctx.logger.info("package candidate continued by a sub-floor box", {
|
|
128701
|
+
tags,
|
|
128702
|
+
meta: {
|
|
128703
|
+
entryId: c.id,
|
|
128704
|
+
hits: c.hits,
|
|
128705
|
+
score: c.score,
|
|
128706
|
+
via: input.via
|
|
128707
|
+
}
|
|
128708
|
+
});
|
|
128333
128709
|
for (const u of outcome.earlyEvidenceUnbacked) this.ctx.logger.info("early pick-up evidence complete — nobody around, left to the TTL", {
|
|
128334
128710
|
tags,
|
|
128335
128711
|
meta: {
|
|
@@ -128357,6 +128733,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
128357
128733
|
observedDwellMs,
|
|
128358
128734
|
requiredDwellMs,
|
|
128359
128735
|
activityBackedAtOpen: ex.activityBackedAtOpen,
|
|
128736
|
+
lastZoneMotionAt: this.frameState.lastPackageZoneMotionAt.get(input.deviceId) ?? null,
|
|
128360
128737
|
reasons
|
|
128361
128738
|
}
|
|
128362
128739
|
});
|