@camstack/addon-post-analysis 1.2.662 → 1.2.664
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 +12 -0
- package/dist/{clip-model-registry-ELaSS9jq.mjs → clip-model-registry-Ch9Sz-Qb.mjs} +33939 -33004
- package/dist/{clip-model-registry-C3N1NpST.js → clip-model-registry-TeNUAAPP.js} +33939 -33004
- 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-BvOlyiL9.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CXG7YPYq.mjs} +2 -2
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CIt_RDlm.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-4TTlyJ66.mjs +26 -0
- package/dist/pipeline-analytics/{hostInit-BjZchMMh.mjs → hostInit-BZ_D3_sF.mjs} +2 -2
- package/dist/pipeline-analytics/index.js +337 -50
- package/dist/pipeline-analytics/index.mjs +337 -50
- 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-D4zkNcgf.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-vaatUWY6.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-TeNUAAPP.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");
|
|
@@ -59338,56 +59338,143 @@ function sweepOne(deps, deviceId) {
|
|
|
59338
59338
|
}
|
|
59339
59339
|
//#endregion
|
|
59340
59340
|
//#region src/pipeline-analytics/pipeline/package/package-still-capture.ts
|
|
59341
|
-
/**
|
|
59342
|
-
|
|
59341
|
+
/**
|
|
59342
|
+
* The runner's native lease on a look frame (holdFrames 8) is released about
|
|
59343
|
+
* 100 ms after inference, so a handle older than this answers `native-miss` or
|
|
59344
|
+
* `preview-only` — promotion runs seconds to minutes after the look (D60).
|
|
59345
|
+
*/
|
|
59346
|
+
var PACKAGE_NATIVE_HANDLE_MAX_AGE_MS = 1e3;
|
|
59347
|
+
/** Total time a promotion may spend landing its still before it ships without (D60). */
|
|
59348
|
+
var PACKAGE_STILL_BOUND_MS = 6e3;
|
|
59349
|
+
/** One live-source attempt never takes longer than this (a sleeping camera). */
|
|
59350
|
+
var PACKAGE_STILL_ATTEMPT_TIMEOUT_MS = 3e3;
|
|
59351
|
+
/** Pause between two live attempts. */
|
|
59352
|
+
var PACKAGE_STILL_RETRY_PAUSE_MS = 750;
|
|
59353
|
+
var PACKAGE_STILL_LIVE_ATTEMPTS = 3;
|
|
59354
|
+
var realPause = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
59343
59355
|
async function jpegOf(reply) {
|
|
59344
59356
|
if (reply.tier === "ram-fullframe") return "preview-only";
|
|
59345
59357
|
if (reply.jpegBytes !== void 0 && reply.jpegBytes.byteLength > 0) return reply.jpegBytes;
|
|
59346
59358
|
if (reply.bytes !== void 0 && reply.bytes.length === reply.width * reply.height * 3) return encodeRgbCropToJpeg(reply.bytes, reply.width, reply.height);
|
|
59347
59359
|
return "empty-frame";
|
|
59348
59360
|
}
|
|
59361
|
+
/** One live read, cut off after `limitMs` so a hung camera cannot hold the delivery. */
|
|
59362
|
+
async function readLive(source, limitMs) {
|
|
59363
|
+
let timer;
|
|
59364
|
+
const timeout = new Promise((resolve) => {
|
|
59365
|
+
timer = setTimeout(() => resolve(null), limitMs);
|
|
59366
|
+
});
|
|
59367
|
+
try {
|
|
59368
|
+
return await Promise.race([source(), timeout]);
|
|
59369
|
+
} finally {
|
|
59370
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
59371
|
+
}
|
|
59372
|
+
}
|
|
59373
|
+
/** Retry the live source under {@link PACKAGE_STILL_BOUND_MS} until a frame lands (D60). */
|
|
59374
|
+
async function liveStill(input, source) {
|
|
59375
|
+
const now = input.now ?? Date.now;
|
|
59376
|
+
const pause = input.pause ?? realPause;
|
|
59377
|
+
const deadline = now() + PACKAGE_STILL_BOUND_MS;
|
|
59378
|
+
let reason = "live-unavailable";
|
|
59379
|
+
let attempts = 0;
|
|
59380
|
+
while (attempts < PACKAGE_STILL_LIVE_ATTEMPTS) {
|
|
59381
|
+
const remaining = deadline - now();
|
|
59382
|
+
if (remaining <= 0) return {
|
|
59383
|
+
jpeg: null,
|
|
59384
|
+
reason: "bound-exhausted",
|
|
59385
|
+
attempts
|
|
59386
|
+
};
|
|
59387
|
+
attempts += 1;
|
|
59388
|
+
try {
|
|
59389
|
+
const jpeg = await readLive(source, Math.min(PACKAGE_STILL_ATTEMPT_TIMEOUT_MS, remaining));
|
|
59390
|
+
if (jpeg !== null && jpeg.byteLength > 0) return {
|
|
59391
|
+
jpeg,
|
|
59392
|
+
reason,
|
|
59393
|
+
attempts
|
|
59394
|
+
};
|
|
59395
|
+
reason = jpeg === null ? "live-unavailable" : "empty-frame";
|
|
59396
|
+
} catch {
|
|
59397
|
+
reason = "live-unavailable";
|
|
59398
|
+
}
|
|
59399
|
+
if (attempts < PACKAGE_STILL_LIVE_ATTEMPTS) await pause(PACKAGE_STILL_RETRY_PAUSE_MS);
|
|
59400
|
+
}
|
|
59401
|
+
return {
|
|
59402
|
+
jpeg: null,
|
|
59403
|
+
reason,
|
|
59404
|
+
attempts
|
|
59405
|
+
};
|
|
59406
|
+
}
|
|
59349
59407
|
/**
|
|
59350
59408
|
* The entry carrying its still's `keyFrameMediaId`, or the entry unchanged when
|
|
59351
59409
|
* it already had one or none could be stored (said, with the reason).
|
|
59410
|
+
*
|
|
59411
|
+
* Ladder: the native handle while its lease can still be alive, then a frame
|
|
59412
|
+
* that exists now (retried under a bound). Every landed still is logged next to
|
|
59413
|
+
* every miss (D400).
|
|
59352
59414
|
*/
|
|
59353
59415
|
async function withParcelStill(input) {
|
|
59354
59416
|
const { entry, logger } = input;
|
|
59355
59417
|
if (entry.keyFrameMediaId !== void 0) return entry;
|
|
59356
|
-
const
|
|
59418
|
+
const tags = { deviceId: entry.deviceId };
|
|
59419
|
+
const miss = (reason, extra = {}) => {
|
|
59357
59420
|
logger.warn("package still NOT stored — the delivery ships without a picture of the parcel", {
|
|
59358
|
-
tags
|
|
59421
|
+
tags,
|
|
59359
59422
|
meta: {
|
|
59360
59423
|
entryId: entry.id,
|
|
59361
59424
|
reason,
|
|
59362
|
-
...
|
|
59425
|
+
...extra
|
|
59363
59426
|
}
|
|
59364
59427
|
});
|
|
59365
59428
|
return entry;
|
|
59366
59429
|
};
|
|
59367
|
-
if (input.handle === null) return miss("no-frame-handle");
|
|
59368
59430
|
if (input.writer === null) return miss("no-media-store");
|
|
59431
|
+
const now = input.now ?? Date.now;
|
|
59432
|
+
let jpeg = null;
|
|
59433
|
+
let source = "native";
|
|
59434
|
+
let stamp = input.timestamp;
|
|
59435
|
+
let nativeMiss = "no-frame-handle";
|
|
59436
|
+
let liveReason = null;
|
|
59437
|
+
let attempts = 0;
|
|
59369
59438
|
try {
|
|
59370
|
-
|
|
59371
|
-
|
|
59372
|
-
|
|
59373
|
-
|
|
59374
|
-
|
|
59375
|
-
|
|
59376
|
-
|
|
59377
|
-
|
|
59378
|
-
|
|
59379
|
-
|
|
59380
|
-
|
|
59439
|
+
if (input.handle !== null) {
|
|
59440
|
+
const reply = await input.query({
|
|
59441
|
+
handle: input.handle,
|
|
59442
|
+
bbox: {
|
|
59443
|
+
x: 0,
|
|
59444
|
+
y: 0,
|
|
59445
|
+
w: entry.frameWidth,
|
|
59446
|
+
h: entry.frameHeight
|
|
59447
|
+
},
|
|
59448
|
+
acceptJpegBytes: true,
|
|
59449
|
+
encodeJpeg: true,
|
|
59450
|
+
pin: require_clip_model_registry.nodePin(input.handle.nodeId)
|
|
59451
|
+
});
|
|
59452
|
+
if (reply === null) nativeMiss = "native-miss";
|
|
59453
|
+
else {
|
|
59454
|
+
const out = await jpegOf(reply);
|
|
59455
|
+
if (typeof out === "string") nativeMiss = out;
|
|
59456
|
+
else jpeg = out;
|
|
59457
|
+
}
|
|
59458
|
+
}
|
|
59459
|
+
if (jpeg === null && input.live !== null) {
|
|
59460
|
+
const live = await liveStill(input, input.live);
|
|
59461
|
+
attempts = live.attempts;
|
|
59462
|
+
if (live.jpeg !== null) {
|
|
59463
|
+
jpeg = live.jpeg;
|
|
59464
|
+
source = "live";
|
|
59465
|
+
stamp = now();
|
|
59466
|
+
} else liveReason = live.reason;
|
|
59467
|
+
}
|
|
59468
|
+
if (jpeg === null) return miss(liveReason ?? nativeMiss, {
|
|
59469
|
+
...input.handle !== null ? { nativeMiss } : {},
|
|
59470
|
+
...liveReason !== null ? { liveAttempts: attempts } : {}
|
|
59381
59471
|
});
|
|
59382
|
-
if (reply === null) return miss("native-miss");
|
|
59383
|
-
const jpeg = await jpegOf(reply);
|
|
59384
|
-
if (typeof jpeg === "string") return miss(jpeg);
|
|
59385
59472
|
const keyFrameMediaId = await input.writer.putReplacing({
|
|
59386
59473
|
deviceId: entry.deviceId,
|
|
59387
59474
|
ownerKind: "track",
|
|
59388
59475
|
ownerId: entry.id,
|
|
59389
59476
|
kind: "keyFrame",
|
|
59390
|
-
timestamp:
|
|
59477
|
+
timestamp: stamp,
|
|
59391
59478
|
data: Buffer.from(jpeg),
|
|
59392
59479
|
subjectBox: {
|
|
59393
59480
|
x: entry.bbox.x,
|
|
@@ -59398,12 +59485,20 @@ async function withParcelStill(input) {
|
|
|
59398
59485
|
frameHeight: entry.frameHeight
|
|
59399
59486
|
}
|
|
59400
59487
|
});
|
|
59488
|
+
logger.info("package still stored", {
|
|
59489
|
+
tags,
|
|
59490
|
+
meta: {
|
|
59491
|
+
entryId: entry.id,
|
|
59492
|
+
mediaId: keyFrameMediaId,
|
|
59493
|
+
source
|
|
59494
|
+
}
|
|
59495
|
+
});
|
|
59401
59496
|
return {
|
|
59402
59497
|
...entry,
|
|
59403
59498
|
keyFrameMediaId
|
|
59404
59499
|
};
|
|
59405
59500
|
} catch (err) {
|
|
59406
|
-
return miss("write-failed", require_clip_model_registry.errMsg(err));
|
|
59501
|
+
return miss("write-failed", { error: require_clip_model_registry.errMsg(err) });
|
|
59407
59502
|
}
|
|
59408
59503
|
}
|
|
59409
59504
|
/**
|
|
@@ -70790,6 +70885,7 @@ Object.freeze({
|
|
|
70790
70885
|
"pageSize": 50
|
|
70791
70886
|
} },
|
|
70792
70887
|
"device-runs": { "listRuns": { "limit": 50 } },
|
|
70888
|
+
"fan-presets": { "capturePreset": { "steps": [] } },
|
|
70793
70889
|
"llm": { "upsertProfile": { "profile": { "__nested__": {
|
|
70794
70890
|
"timeoutMs": 6e4,
|
|
70795
70891
|
"connectTimeoutMs": 1e4,
|
|
@@ -71030,6 +71126,10 @@ Object.freeze({
|
|
|
71030
71126
|
"speed": 2
|
|
71031
71127
|
}
|
|
71032
71128
|
},
|
|
71129
|
+
"system": { "debugWatch": {
|
|
71130
|
+
"channels": [],
|
|
71131
|
+
"deviceIds": []
|
|
71132
|
+
} },
|
|
71033
71133
|
"terminal-session": { "pullOutput": { "waitMs": 0 } },
|
|
71034
71134
|
"training": {
|
|
71035
71135
|
"planJob": { "selection": { "__nested__": { "workState": "complete" } } },
|
|
@@ -71076,6 +71176,7 @@ Object.freeze({
|
|
|
71076
71176
|
],
|
|
71077
71177
|
"device-removal-hook": ["listDeviceData"],
|
|
71078
71178
|
"failure-contribution": ["list"],
|
|
71179
|
+
"integration-instances": ["listInstances"],
|
|
71079
71180
|
"llm": [
|
|
71080
71181
|
"getDefaults",
|
|
71081
71182
|
"getUsage",
|
|
@@ -71117,6 +71218,7 @@ Object.freeze({
|
|
|
71117
71218
|
"listSupportedEntityKinds",
|
|
71118
71219
|
"unexposeEntity"
|
|
71119
71220
|
],
|
|
71221
|
+
"integration-instances": ["testInstance"],
|
|
71120
71222
|
"log-destination": [
|
|
71121
71223
|
"cancelRead",
|
|
71122
71224
|
"describeRead",
|
|
@@ -85215,6 +85317,9 @@ async function renderMosaic(request, logger) {
|
|
|
85215
85317
|
* one composition are decisions about the set, and none of them belongs in a
|
|
85216
85318
|
* shelf that photographs one camera for an absence.
|
|
85217
85319
|
*
|
|
85320
|
+
* A SLEEPING battery camera is woken by one forced capture when the rule leaves
|
|
85321
|
+
* it allowed (D998) — bounded, once per gap per camera, never by a poll.
|
|
85322
|
+
*
|
|
85218
85323
|
* Two reads can only ever ADD a camera back, never take one away on their own
|
|
85219
85324
|
* failure (D49): a wrapper-state read (run in parallel with the capture) that
|
|
85220
85325
|
* misses or throws keeps the tile, and a freshness read that misses or throws
|
|
@@ -85228,6 +85333,15 @@ var NC_LINKED_CAMERA_PREFIX = "nc-linked:";
|
|
|
85228
85333
|
function isLinkedStillId(id) {
|
|
85229
85334
|
return id.startsWith(NC_LINKED_MOSAIC_PREFIX) || id.startsWith(NC_LINKED_CAMERA_PREFIX);
|
|
85230
85335
|
}
|
|
85336
|
+
/**
|
|
85337
|
+
* D998 — the one forced capture that wakes a sleeping battery camera for a
|
|
85338
|
+
* sensor notification: the bound it may take, the pause between re-polls of a
|
|
85339
|
+
* camera another wake already started, and the gap per camera before it is
|
|
85340
|
+
* woken again (a flapping contact must not drain a battery).
|
|
85341
|
+
*/
|
|
85342
|
+
var NC_LINKED_WAKE_CAPTURE_TIMEOUT_MS = 15e3;
|
|
85343
|
+
var NC_LINKED_WAKE_POLL_MS = 1e3;
|
|
85344
|
+
var NC_LINKED_WAKE_MIN_GAP_MS = 6e4;
|
|
85231
85345
|
/** Per camera — the marker's own bound (`SENSOR_MARKER_SNAPSHOT_TIMEOUT_MS`). */
|
|
85232
85346
|
var SENSOR_LINKED_CAPTURE_TIMEOUT_MS = 2500;
|
|
85233
85347
|
/** The capture-free wrapper-state read, per camera. It runs IN PARALLEL with
|
|
@@ -85260,11 +85374,14 @@ var NcLinkedStillShelf = class {
|
|
|
85260
85374
|
deps;
|
|
85261
85375
|
sets = /* @__PURE__ */ new Map();
|
|
85262
85376
|
owners = /* @__PURE__ */ new Map();
|
|
85377
|
+
/** setId → when its in-flight wake gives up (the dispatcher waits that long). */
|
|
85378
|
+
pendingWakes = /* @__PURE__ */ new Map();
|
|
85379
|
+
lastWakeAt = /* @__PURE__ */ new Map();
|
|
85263
85380
|
constructor(deps) {
|
|
85264
85381
|
this.deps = deps;
|
|
85265
85382
|
}
|
|
85266
85383
|
/** SYNCHRONOUS: the evaluation that follows names these owners. */
|
|
85267
|
-
captureSet(sensorId, cameraIds, atMs) {
|
|
85384
|
+
captureSet(sensorId, cameraIds, atMs, options = { wakeSleeping: false }) {
|
|
85268
85385
|
const now = this.deps.now();
|
|
85269
85386
|
this.prune(now);
|
|
85270
85387
|
const setId = (0, node_crypto.randomUUID)();
|
|
@@ -85292,7 +85409,7 @@ var NcLinkedStillShelf = class {
|
|
|
85292
85409
|
camera: cameraId
|
|
85293
85410
|
});
|
|
85294
85411
|
});
|
|
85295
|
-
this.run(setId, stills, atMs, now).catch((err) => {
|
|
85412
|
+
this.run(setId, stills, atMs, now, options).catch((err) => {
|
|
85296
85413
|
this.deps.logger.warn("sensor notification pictures failed — it ships text-only", {
|
|
85297
85414
|
tags: { deviceId: sensorId },
|
|
85298
85415
|
meta: { error: String(err) }
|
|
@@ -85300,6 +85417,15 @@ var NcLinkedStillShelf = class {
|
|
|
85300
85417
|
});
|
|
85301
85418
|
return stills;
|
|
85302
85419
|
}
|
|
85420
|
+
/**
|
|
85421
|
+
* While a wake of one of this owner's cameras is in flight: the instant it
|
|
85422
|
+
* gives up (its bound plus one poll). `undefined` otherwise — the dispatcher
|
|
85423
|
+
* then keeps its ordinary still-wait.
|
|
85424
|
+
*/
|
|
85425
|
+
pendingUntil(ownerId) {
|
|
85426
|
+
const ref = this.owners.get(ownerId);
|
|
85427
|
+
return ref === void 0 ? void 0 : this.pendingWakes.get(ref.setId);
|
|
85428
|
+
}
|
|
85303
85429
|
/** `undefined` = not an owner this shelf minted (or pruned); `[]` = nothing (yet). */
|
|
85304
85430
|
get(ownerId) {
|
|
85305
85431
|
const ref = this.owners.get(ownerId);
|
|
@@ -85321,20 +85447,37 @@ var NcLinkedStillShelf = class {
|
|
|
85321
85447
|
clear() {
|
|
85322
85448
|
this.sets.clear();
|
|
85323
85449
|
this.owners.clear();
|
|
85450
|
+
this.pendingWakes.clear();
|
|
85324
85451
|
}
|
|
85325
|
-
async run(setId, stills, atMs, startedAt) {
|
|
85326
|
-
const
|
|
85452
|
+
async run(setId, stills, atMs, startedAt, options) {
|
|
85453
|
+
const ctx = {
|
|
85454
|
+
setId,
|
|
85455
|
+
sensorId: stills.sensorId,
|
|
85456
|
+
startedAt,
|
|
85457
|
+
options
|
|
85458
|
+
};
|
|
85459
|
+
let results;
|
|
85460
|
+
try {
|
|
85461
|
+
results = await Promise.all(stills.cameraIds.map((id) => this.captureOne(id, ctx)));
|
|
85462
|
+
} finally {
|
|
85463
|
+
this.pendingWakes.delete(setId);
|
|
85464
|
+
}
|
|
85327
85465
|
const judged = await this.judgeFreshness(stills.sensorId, results, atMs);
|
|
85328
85466
|
for (const r of judged) this.logTile(stills.sensorId, r);
|
|
85329
85467
|
const landed = judged.filter(isLandedTile);
|
|
85330
85468
|
const composed = await this.compose(stills.sensorId, landed, atMs);
|
|
85331
85469
|
const undrawable = landed.length - composed.drawn;
|
|
85470
|
+
const woken = judged.filter((r) => r.woken === true).map((r) => r.cameraId);
|
|
85332
85471
|
this.deps.logger.info(composed.mosaic.length === 0 ? "sensor notification has no linked picture — it ships text-only" : "sensor notification pictures ready", {
|
|
85333
85472
|
tags: { deviceId: stills.sensorId },
|
|
85334
85473
|
meta: {
|
|
85335
85474
|
cameras: stills.cameraIds.length,
|
|
85336
85475
|
landed: composed.drawn,
|
|
85337
85476
|
...undrawable > 0 ? { undrawable } : {},
|
|
85477
|
+
...woken.length > 0 ? {
|
|
85478
|
+
woken,
|
|
85479
|
+
waitedMs: this.deps.now() - startedAt
|
|
85480
|
+
} : {},
|
|
85338
85481
|
dropped: Object.fromEntries(judged.filter((r) => r.outcome !== "landed").map((r) => [String(r.cameraId), r.outcome])),
|
|
85339
85482
|
tookMs: this.deps.now() - startedAt
|
|
85340
85483
|
}
|
|
@@ -85347,21 +85490,25 @@ var NcLinkedStillShelf = class {
|
|
|
85347
85490
|
mosaic: composed.mosaic
|
|
85348
85491
|
});
|
|
85349
85492
|
}
|
|
85350
|
-
async captureOne(cameraId,
|
|
85351
|
-
const took = () => this.deps.now() - startedAt;
|
|
85493
|
+
async captureOne(cameraId, ctx) {
|
|
85494
|
+
const took = () => this.deps.now() - ctx.startedAt;
|
|
85352
85495
|
if (this.deps.isOnline(cameraId) === false) return {
|
|
85353
85496
|
cameraId,
|
|
85354
85497
|
outcome: "excluded",
|
|
85355
85498
|
reason: "offline",
|
|
85356
85499
|
tookMs: took()
|
|
85357
85500
|
};
|
|
85358
|
-
const [verdict, capture] = await Promise.all([this.wrapperVerdict(cameraId), this.captureStill(cameraId)]);
|
|
85359
|
-
|
|
85501
|
+
const [verdict, capture] = await Promise.all([this.wrapperVerdict(cameraId), this.captureStill(cameraId, false, SENSOR_LINKED_CAPTURE_TIMEOUT_MS)]);
|
|
85502
|
+
const excluded = (reason) => ({
|
|
85360
85503
|
cameraId,
|
|
85361
85504
|
outcome: "excluded",
|
|
85362
|
-
reason
|
|
85505
|
+
reason,
|
|
85363
85506
|
tookMs: took()
|
|
85364
|
-
};
|
|
85507
|
+
});
|
|
85508
|
+
if (verdict.reason === "sleeping") return this.wakeOne(cameraId, ctx);
|
|
85509
|
+
if (verdict.reason === "waking" && ctx.options.wakeSleeping) return this.awaitWaking(cameraId, ctx);
|
|
85510
|
+
if (verdict.reason === "unreachable" && ctx.options.wakeSleeping) return excluded("wake-skipped:unreachable");
|
|
85511
|
+
if (verdict.reason !== null) return excluded(verdict.reason);
|
|
85365
85512
|
return {
|
|
85366
85513
|
cameraId,
|
|
85367
85514
|
...capture,
|
|
@@ -85369,9 +85516,84 @@ var NcLinkedStillShelf = class {
|
|
|
85369
85516
|
tookMs: took()
|
|
85370
85517
|
};
|
|
85371
85518
|
}
|
|
85372
|
-
|
|
85519
|
+
/** D998: one forced capture, by the rule's leave, at most once per gap per camera. */
|
|
85520
|
+
async wakeOne(cameraId, ctx) {
|
|
85521
|
+
const took = () => this.deps.now() - ctx.startedAt;
|
|
85522
|
+
const skipped = (reason) => ({
|
|
85523
|
+
cameraId,
|
|
85524
|
+
outcome: "excluded",
|
|
85525
|
+
reason,
|
|
85526
|
+
tookMs: took()
|
|
85527
|
+
});
|
|
85528
|
+
if (!ctx.options.wakeSleeping) return skipped("wake-skipped:policy");
|
|
85529
|
+
const last = this.lastWakeAt.get(cameraId);
|
|
85530
|
+
if (last !== void 0 && ctx.startedAt - last < NC_LINKED_WAKE_MIN_GAP_MS) return skipped("wake-skipped:recent-wake");
|
|
85531
|
+
this.lastWakeAt.set(cameraId, ctx.startedAt);
|
|
85532
|
+
this.markPending(ctx);
|
|
85533
|
+
this.deps.logger.info("linked battery camera woken for the sensor notification", {
|
|
85534
|
+
tags: { deviceId: cameraId },
|
|
85535
|
+
meta: {
|
|
85536
|
+
sensorId: ctx.sensorId,
|
|
85537
|
+
ruleIds: ctx.options.ruleIds ?? [],
|
|
85538
|
+
boundMs: NC_LINKED_WAKE_CAPTURE_TIMEOUT_MS
|
|
85539
|
+
}
|
|
85540
|
+
});
|
|
85541
|
+
const capture = await this.captureStill(cameraId, true, NC_LINKED_WAKE_CAPTURE_TIMEOUT_MS);
|
|
85542
|
+
return {
|
|
85543
|
+
cameraId,
|
|
85544
|
+
...this.asWakeResult(capture),
|
|
85545
|
+
woken: true,
|
|
85546
|
+
tookMs: took()
|
|
85547
|
+
};
|
|
85548
|
+
}
|
|
85549
|
+
/** A camera another wake already started: never forced, re-polled within the bound. */
|
|
85550
|
+
async awaitWaking(cameraId, ctx) {
|
|
85551
|
+
const took = () => this.deps.now() - ctx.startedAt;
|
|
85552
|
+
this.markPending(ctx);
|
|
85553
|
+
const polls = Math.floor(NC_LINKED_WAKE_CAPTURE_TIMEOUT_MS / NC_LINKED_WAKE_POLL_MS);
|
|
85554
|
+
for (let i = 0; i < polls; i++) {
|
|
85555
|
+
await new Promise((resolve) => setTimeout(resolve, NC_LINKED_WAKE_POLL_MS));
|
|
85556
|
+
const verdict = await this.wrapperVerdict(cameraId);
|
|
85557
|
+
if (verdict.reason === "waking") continue;
|
|
85558
|
+
if (verdict.reason !== null) return {
|
|
85559
|
+
cameraId,
|
|
85560
|
+
outcome: "excluded",
|
|
85561
|
+
reason: verdict.reason,
|
|
85562
|
+
tookMs: took()
|
|
85563
|
+
};
|
|
85564
|
+
const capture = await this.captureStill(cameraId, false, SENSOR_LINKED_CAPTURE_TIMEOUT_MS);
|
|
85565
|
+
return {
|
|
85566
|
+
cameraId,
|
|
85567
|
+
...this.asWakeResult(capture),
|
|
85568
|
+
woken: true,
|
|
85569
|
+
tookMs: took()
|
|
85570
|
+
};
|
|
85571
|
+
}
|
|
85572
|
+
return {
|
|
85573
|
+
cameraId,
|
|
85574
|
+
outcome: "wake-timeout",
|
|
85575
|
+
woken: true,
|
|
85576
|
+
tookMs: took()
|
|
85577
|
+
};
|
|
85578
|
+
}
|
|
85579
|
+
markPending(ctx) {
|
|
85580
|
+
const until = ctx.startedAt + NC_LINKED_WAKE_CAPTURE_TIMEOUT_MS + NC_LINKED_WAKE_POLL_MS;
|
|
85581
|
+
this.pendingWakes.set(ctx.setId, Math.max(this.pendingWakes.get(ctx.setId) ?? 0, until));
|
|
85582
|
+
}
|
|
85583
|
+
/** A wake that brought nothing back names itself, not the generic `empty`. */
|
|
85584
|
+
asWakeResult(capture) {
|
|
85585
|
+
if (capture.outcome === "timeout") return {
|
|
85586
|
+
...capture,
|
|
85587
|
+
outcome: "wake-timeout"
|
|
85588
|
+
};
|
|
85589
|
+
return capture.outcome === "empty" || capture.outcome === "error" && capture.reason !== "undecodable" ? {
|
|
85590
|
+
...capture,
|
|
85591
|
+
outcome: "wake-refused"
|
|
85592
|
+
} : capture;
|
|
85593
|
+
}
|
|
85594
|
+
async captureStill(cameraId, force, timeoutMs) {
|
|
85373
85595
|
try {
|
|
85374
|
-
const shot = await withTimeout$2(this.deps.getSnapshot(cameraId),
|
|
85596
|
+
const shot = await withTimeout$2(force ? this.deps.getSnapshot(cameraId, { force: true }) : this.deps.getSnapshot(cameraId), timeoutMs, "snapshot cap");
|
|
85375
85597
|
if (shot === null || shot.base64.length === 0) return { outcome: "empty" };
|
|
85376
85598
|
const jpeg = await this.decodableStill(Buffer.from(shot.base64, "base64"));
|
|
85377
85599
|
if (jpeg === null) return {
|
|
@@ -86930,6 +87152,13 @@ var NC_RECORDED_FOOTAGE_WAIT_MS = 3e4;
|
|
|
86930
87152
|
var NC_STILL_WAIT_MS = 3e3;
|
|
86931
87153
|
var NC_STILL_WAIT_INTERVAL_MS = 500;
|
|
86932
87154
|
/**
|
|
87155
|
+
* D998 — the longest a sensor notification holds for a linked battery camera
|
|
87156
|
+
* that is being woken (`stillDeadline`). A `sent` row is final, so the picture
|
|
87157
|
+
* cannot follow the push: it is waited for here, bounded, or never attached.
|
|
87158
|
+
* Owners with no wake in flight keep {@link NC_STILL_WAIT_MS}.
|
|
87159
|
+
*/
|
|
87160
|
+
var NC_LINKED_WAKE_WAIT_MS = 15e3;
|
|
87161
|
+
/**
|
|
86933
87162
|
* How far a still's OWN instant may sit from the trigger and still count as
|
|
86934
87163
|
* being ABOUT the trigger, for a `delivery: 'immediate'` rule.
|
|
86935
87164
|
*
|
|
@@ -88146,7 +88375,15 @@ var NcDispatcher = class {
|
|
|
88146
88375
|
const firstLook = groupTiles !== null ? null : await this.resolveAttachment(entry, canStillGainMedia(entry) ? "trigger-only" : "any");
|
|
88147
88376
|
const footagePending = this.renderFootage(entry);
|
|
88148
88377
|
const mosaic = groupView !== void 0 && groupTiles !== null ? await this.groupedMosaic(entry, groupView, groupTiles) : null;
|
|
88149
|
-
|
|
88378
|
+
let waitedMs = 0;
|
|
88379
|
+
let still;
|
|
88380
|
+
if (mosaic !== null) still = mosaic.attachment;
|
|
88381
|
+
else if (firstLook !== null || entry.payload.media === "none") still = firstLook;
|
|
88382
|
+
else {
|
|
88383
|
+
const waited = await this.waitForStill(entry);
|
|
88384
|
+
still = waited.still;
|
|
88385
|
+
waitedMs = waited.waitedMs;
|
|
88386
|
+
}
|
|
88150
88387
|
const grouped = groupView !== void 0 && groupMembers !== null ? {
|
|
88151
88388
|
windowKey: groupView.key,
|
|
88152
88389
|
members: groupView.members.length,
|
|
@@ -88171,6 +88408,7 @@ var NcDispatcher = class {
|
|
|
88171
88408
|
recordKind: entry.recordKind,
|
|
88172
88409
|
recordId: entry.recordId,
|
|
88173
88410
|
policy: entry.payload.media,
|
|
88411
|
+
waitedMs,
|
|
88174
88412
|
...entry.payload.mediaImage !== void 0 ? { image: entry.payload.mediaImage } : {},
|
|
88175
88413
|
...memberTrackIds.length > 1 ? { memberTrackIds } : {},
|
|
88176
88414
|
owners: [...entry.payload.subject.eventId !== void 0 ? [`object:${entry.payload.subject.eventId}`] : [], ...entry.payload.subject.trackId !== void 0 ? [`track:${entry.payload.subject.trackId}`] : []]
|
|
@@ -88197,7 +88435,13 @@ var NcDispatcher = class {
|
|
|
88197
88435
|
const subject = entry.payload.subject;
|
|
88198
88436
|
const out = [];
|
|
88199
88437
|
for (const { ownerId, cameraId } of subject.extraStills ?? []) {
|
|
88200
|
-
|
|
88438
|
+
let file = await this.linkedStillFile(entry, ownerId, cameraId);
|
|
88439
|
+
for (let polls = 0; file === void 0 && polls < NC_LINKED_WAKE_WAIT_MS / NC_STILL_WAIT_INTERVAL_MS; polls += 1) {
|
|
88440
|
+
const deadline = this.deps.stillDeadline?.(ownerId);
|
|
88441
|
+
if (deadline === void 0 || this.now() >= deadline) break;
|
|
88442
|
+
await this.sleep(NC_STILL_WAIT_INTERVAL_MS);
|
|
88443
|
+
file = await this.linkedStillFile(entry, ownerId, cameraId);
|
|
88444
|
+
}
|
|
88201
88445
|
const bytes = file === void 0 ? null : decodeStillBytes(file.base64);
|
|
88202
88446
|
if (file === void 0 || bytes === null) {
|
|
88203
88447
|
this.deps.logger.info("linked camera picture missing at send — skipped", {
|
|
@@ -88684,11 +88928,19 @@ var NcDispatcher = class {
|
|
|
88684
88928
|
*/
|
|
88685
88929
|
async waitForStill(entry) {
|
|
88686
88930
|
const subject = entry.payload.subject;
|
|
88687
|
-
if (this.now() - subject.timestamp > NC_STILL_WAIT_MAX_AGE_MS) return
|
|
88688
|
-
|
|
88689
|
-
|
|
88690
|
-
|
|
88931
|
+
if (this.now() - subject.timestamp > NC_STILL_WAIT_MAX_AGE_MS) return {
|
|
88932
|
+
still: await this.resolveAttachment(entry, "any"),
|
|
88933
|
+
waitedMs: 0
|
|
88934
|
+
};
|
|
88935
|
+
if (subject.eventId === void 0 && subject.trackId === void 0) return {
|
|
88936
|
+
still: null,
|
|
88937
|
+
waitedMs: 0
|
|
88938
|
+
};
|
|
88939
|
+
const startedAt = this.now();
|
|
88940
|
+
let waitedMs = 0;
|
|
88941
|
+
for (let i = 0; waitedMs < this.stillWaitBoundMs(entry, startedAt); i += 1) {
|
|
88691
88942
|
await this.sleep(NC_STILL_WAIT_INTERVAL_MS);
|
|
88943
|
+
waitedMs += NC_STILL_WAIT_INTERVAL_MS;
|
|
88692
88944
|
const still = await this.resolveAttachment(entry, "trigger-only");
|
|
88693
88945
|
if (still !== null) {
|
|
88694
88946
|
this.deps.logger.info("the still landed during the bounded wait", {
|
|
@@ -88700,10 +88952,32 @@ var NcDispatcher = class {
|
|
|
88700
88952
|
kind: still.name
|
|
88701
88953
|
}
|
|
88702
88954
|
});
|
|
88703
|
-
return
|
|
88955
|
+
return {
|
|
88956
|
+
still,
|
|
88957
|
+
waitedMs
|
|
88958
|
+
};
|
|
88704
88959
|
}
|
|
88705
88960
|
}
|
|
88706
|
-
return
|
|
88961
|
+
return {
|
|
88962
|
+
still: await this.resolveAttachment(entry, "any"),
|
|
88963
|
+
waitedMs
|
|
88964
|
+
};
|
|
88965
|
+
}
|
|
88966
|
+
/**
|
|
88967
|
+
* How long this row's still poll may run: {@link NC_STILL_WAIT_MS}, or — while
|
|
88968
|
+
* the shelf says a linked camera of this row is being woken — until that
|
|
88969
|
+
* wake gives up, at most {@link NC_LINKED_WAKE_WAIT_MS} (D998).
|
|
88970
|
+
*/
|
|
88971
|
+
stillWaitBoundMs(entry, startedAt) {
|
|
88972
|
+
const subject = entry.payload.subject;
|
|
88973
|
+
const owners = [...subject.trackId !== void 0 ? [subject.trackId] : [], ...(subject.extraStills ?? []).map((extra) => extra.ownerId)];
|
|
88974
|
+
let deadline;
|
|
88975
|
+
for (const ownerId of owners) {
|
|
88976
|
+
const until = this.deps.stillDeadline?.(ownerId);
|
|
88977
|
+
if (until !== void 0 && (deadline === void 0 || until > deadline)) deadline = until;
|
|
88978
|
+
}
|
|
88979
|
+
if (deadline === void 0) return NC_STILL_WAIT_MS;
|
|
88980
|
+
return Math.max(NC_STILL_WAIT_MS, Math.min(NC_LINKED_WAKE_WAIT_MS, deadline - startedAt));
|
|
88707
88981
|
}
|
|
88708
88982
|
/**
|
|
88709
88983
|
* The grouped MOSAIC (D692 §6.2): one tile per member, at most
|
|
@@ -88851,8 +89125,8 @@ var NcDispatcher = class {
|
|
|
88851
89125
|
return ts !== void 0 && this.now() - ts <= NC_STILL_WAIT_MAX_AGE_MS;
|
|
88852
89126
|
});
|
|
88853
89127
|
await look("trigger-only", pollable());
|
|
88854
|
-
const
|
|
88855
|
-
for (let
|
|
89128
|
+
const startedAt = this.now();
|
|
89129
|
+
for (let waited = 0; waited < this.stillWaitBoundMs(entry, startedAt) && pollable().length > 0; waited += NC_STILL_WAIT_INTERVAL_MS) {
|
|
88856
89130
|
await this.sleep(NC_STILL_WAIT_INTERVAL_MS);
|
|
88857
89131
|
await look("trigger-only", pollable());
|
|
88858
89132
|
}
|
|
@@ -104372,7 +104646,7 @@ function beginLinkedStills(host, request) {
|
|
|
104372
104646
|
if (rowCameraIds.length > 0 && rowShown.length === 0) return void 0;
|
|
104373
104647
|
const shown = host.unmutedCameras(cameraIds);
|
|
104374
104648
|
const involved = [sensorId, ...rowShown];
|
|
104375
|
-
|
|
104649
|
+
const wanting = host.rules.listEnabled("device-event").filter((rule) => {
|
|
104376
104650
|
if (ruleShipsNoStill(rule)) return false;
|
|
104377
104651
|
if (!deviceEventRuleMayFireOnKind(rule, sensorKind, sensorRaised)) return false;
|
|
104378
104652
|
const excluded = rule.conditions.devicesExclude;
|
|
@@ -104383,9 +104657,14 @@ function beginLinkedStills(host, request) {
|
|
|
104383
104657
|
})) return false;
|
|
104384
104658
|
const devices = rule.conditions.devices;
|
|
104385
104659
|
return devices === void 0 || devices.length === 0 || devices.some((id) => involved.includes(id));
|
|
104386
|
-
})
|
|
104660
|
+
});
|
|
104661
|
+
if (wanting.length === 0) return void 0;
|
|
104662
|
+
const wakeSleeping = wanting.some((rule) => rule.media?.wakeSleepingCamera !== false);
|
|
104387
104663
|
for (const cameraId of cameraIds) if (!shown.includes(cameraId)) host.reportLinkedCameraMuted(cameraId, sensorId);
|
|
104388
|
-
return shown.length > 0 ? shelf.captureSet(sensorId, shown, atMs
|
|
104664
|
+
return shown.length > 0 ? shelf.captureSet(sensorId, shown, atMs, {
|
|
104665
|
+
wakeSleeping,
|
|
104666
|
+
ruleIds: wanting.map((rule) => rule.id)
|
|
104667
|
+
}) : void 0;
|
|
104389
104668
|
}
|
|
104390
104669
|
/**
|
|
104391
104670
|
* A muted camera left out of a linked set — one INFO line per camera per
|
|
@@ -107108,6 +107387,7 @@ var NotificationCenter = class {
|
|
|
107108
107387
|
...deps.dispatcher,
|
|
107109
107388
|
logger: this.logger.child("dispatch"),
|
|
107110
107389
|
texts: this.texts,
|
|
107390
|
+
stillDeadline: (ownerId) => isLinkedStillId(ownerId) ? this.linkedStills?.pendingUntil(ownerId) : void 0,
|
|
107111
107391
|
getMediaForOwner: async (ownerKind, ownerId, eventScope) => {
|
|
107112
107392
|
const held = isSyntheticId(ownerId) ? this.syntheticMedia.get(ownerId) : void 0;
|
|
107113
107393
|
if (held !== void 0) return held.files;
|
|
@@ -109691,7 +109971,10 @@ var BuildNotificationCenter = class extends BuildNotificationCenterBase {
|
|
|
109691
109971
|
},
|
|
109692
109972
|
readAlerts: () => api.alerts.list.query({ limit: 5e3 }),
|
|
109693
109973
|
emitBusEvent: (event) => this.ctx.eventBus.emit(event),
|
|
109694
|
-
getSnapshot: (deviceId) => api.snapshot.getSnapshot.query(
|
|
109974
|
+
getSnapshot: (deviceId, opts) => api.snapshot.getSnapshot.query(opts?.force === true ? {
|
|
109975
|
+
deviceId,
|
|
109976
|
+
force: true
|
|
109977
|
+
} : { deviceId }),
|
|
109695
109978
|
recognitionOutlook: ({ deviceId, trackId, className, labelKind, now }) => recognitionOutlook({
|
|
109696
109979
|
className,
|
|
109697
109980
|
...labelKind !== void 0 ? { labelKind } : {},
|
|
@@ -128056,11 +128339,15 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
128056
128339
|
const lookFrame = this.frameState.packageLookFrames.get(input.deviceId);
|
|
128057
128340
|
promoteWithStill({
|
|
128058
128341
|
entry,
|
|
128059
|
-
handle: lookFrame !== void 0 && input.timestamp - lookFrame.at <=
|
|
128342
|
+
handle: lookFrame !== void 0 && input.timestamp - lookFrame.at <= PACKAGE_NATIVE_HANDLE_MAX_AGE_MS ? lookFrame.handle : null,
|
|
128060
128343
|
timestamp: lookFrame?.at ?? input.timestamp,
|
|
128061
128344
|
query: (request) => this.queryPackageSceneCrop(request),
|
|
128062
128345
|
writer: this.mediaStore,
|
|
128063
128346
|
logger: this.ctx.logger,
|
|
128347
|
+
live: async () => {
|
|
128348
|
+
const snap = await this.ctx.api.snapshot.getSnapshot.query({ deviceId: input.deviceId });
|
|
128349
|
+
return snap === null ? null : Uint8Array.from(Buffer.from(snap.base64, "base64"));
|
|
128350
|
+
},
|
|
128064
128351
|
detector: this.packageDropDetector
|
|
128065
128352
|
}).catch((err) => {
|
|
128066
128353
|
this.ctx.logger.warn("package promotion failed", {
|