@camstack/addon-post-analysis 1.2.601 → 1.2.602
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/{clip-model-registry-DAZ9ie-f.js → clip-model-registry-D0-VNIW5.js} +381 -8
- package/dist/{clip-model-registry-BuM8eXlN.mjs → clip-model-registry-lQ7QWjLf.mjs} +376 -9
- 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-DAs_96VP.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-BPA0T5-F.mjs} +2 -2
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-1AlitJg2.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-Cfj8Mv5X.mjs +26 -0
- package/dist/pipeline-analytics/{hostInit-C3wjY7W8.mjs → hostInit-DIjMiIYW.mjs} +2 -2
- package/dist/pipeline-analytics/index.js +281 -503
- package/dist/pipeline-analytics/index.mjs +281 -503
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -1
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CElkWYPg.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-SRvLcL_J.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-D0-VNIW5.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");
|
|
@@ -6938,17 +6938,6 @@ function ncAudioSampleFromFrame(frame, timestampMs, floor) {
|
|
|
6938
6938
|
...frame.classifyOutcome !== void 0 ? { classifyOutcome: frame.classifyOutcome } : {}
|
|
6939
6939
|
};
|
|
6940
6940
|
}
|
|
6941
|
-
/** The level-path audio row for one frame — the one row kind with no label to explain it. */
|
|
6942
|
-
function levelAudioEventDraft(deviceId, timestamp, frame) {
|
|
6943
|
-
return {
|
|
6944
|
-
deviceId,
|
|
6945
|
-
timestamp,
|
|
6946
|
-
kind: "audio",
|
|
6947
|
-
rms: frame.level?.rms ?? 0,
|
|
6948
|
-
dbfs: frame.level?.dbfs ?? 0,
|
|
6949
|
-
...frame.classifyOutcome !== void 0 ? { classifyOutcome: frame.classifyOutcome } : {}
|
|
6950
|
-
};
|
|
6951
|
-
}
|
|
6952
6941
|
//#endregion
|
|
6953
6942
|
//#region src/pipeline-analytics/scene/condition-resolver.ts
|
|
6954
6943
|
var DAY = "day";
|
|
@@ -10930,7 +10919,7 @@ var NC_LIVENESS_SPEC = {
|
|
|
10930
10919
|
writeMode: "write-behind",
|
|
10931
10920
|
keyOf: (row) => row.subject,
|
|
10932
10921
|
toValue: rowToValue$1,
|
|
10933
|
-
fromRecord: recordToRow$
|
|
10922
|
+
fromRecord: recordToRow$1,
|
|
10934
10923
|
equalFact: (held, incoming) => held.state === incoming.state,
|
|
10935
10924
|
deviceIdOf: (row) => row.deviceId
|
|
10936
10925
|
};
|
|
@@ -11117,7 +11106,7 @@ function rowToValue$1(row) {
|
|
|
11117
11106
|
* (silent) rather than hydrating a state nothing can ever equal, which would
|
|
11118
11107
|
* make every future event a phantom flip.
|
|
11119
11108
|
*/
|
|
11120
|
-
function recordToRow$
|
|
11109
|
+
function recordToRow$1(subject, data) {
|
|
11121
11110
|
if (subject.length === 0) return null;
|
|
11122
11111
|
const state = data["state"];
|
|
11123
11112
|
if (state !== "online" && state !== "offline") return null;
|
|
@@ -12598,7 +12587,7 @@ var NC_OCCUPANCY_SPEC = {
|
|
|
12598
12587
|
writeMode: "write-through",
|
|
12599
12588
|
keyOf: (row) => row.key,
|
|
12600
12589
|
toValue: rowToValue,
|
|
12601
|
-
fromRecord: recordToRow
|
|
12590
|
+
fromRecord: recordToRow,
|
|
12602
12591
|
deviceIdOf: (row) => row.deviceId
|
|
12603
12592
|
};
|
|
12604
12593
|
var OccupancyStore = class {
|
|
@@ -12675,7 +12664,7 @@ function rowToValue(row) {
|
|
|
12675
12664
|
* {@link parseOccupancyKey} rejects — so the caller skips it (cold re-observe)
|
|
12676
12665
|
* rather than hydrating a mis-parsed scope.
|
|
12677
12666
|
*/
|
|
12678
|
-
function recordToRow
|
|
12667
|
+
function recordToRow(key, data) {
|
|
12679
12668
|
const scope = parseOccupancyKey(key);
|
|
12680
12669
|
if (scope === null) return null;
|
|
12681
12670
|
const deviceId = Number(data["deviceId"]);
|
|
@@ -50543,7 +50532,7 @@ function previewBlurSigma(frameWidth) {
|
|
|
50543
50532
|
}
|
|
50544
50533
|
/** Break a word too long for one line into line-width pieces. Without this a
|
|
50545
50534
|
* single 200-character token wraps to nothing and overflows both edges. */
|
|
50546
|
-
function chunk$
|
|
50535
|
+
function chunk$1(word, size) {
|
|
50547
50536
|
const pieces = [];
|
|
50548
50537
|
for (let i = 0; i < word.length; i += size) pieces.push(word.slice(i, i + size));
|
|
50549
50538
|
return pieces;
|
|
@@ -50551,7 +50540,7 @@ function chunk$2(word, size) {
|
|
|
50551
50540
|
/** Greedy word wrap at a character budget — the only measurement available,
|
|
50552
50541
|
* since librsvg lays the text out long after this string is written. */
|
|
50553
50542
|
function wrapLines(text, maxChars) {
|
|
50554
|
-
return text.split(" ").filter((w) => w.length > 0).reduce((lines, word) => (word.length > maxChars ? chunk$
|
|
50543
|
+
return text.split(" ").filter((w) => w.length > 0).reduce((lines, word) => (word.length > maxChars ? chunk$1(word, maxChars) : [word]).reduce((acc, piece) => {
|
|
50555
50544
|
const last = acc.at(-1);
|
|
50556
50545
|
if (last === void 0) return [piece];
|
|
50557
50546
|
const merged = `${last} ${piece}`;
|
|
@@ -60482,7 +60471,7 @@ async function readRescoreFingerprint(ports) {
|
|
|
60482
60471
|
clusterModelId
|
|
60483
60472
|
};
|
|
60484
60473
|
}
|
|
60485
|
-
function chunk
|
|
60474
|
+
function chunk(items, size) {
|
|
60486
60475
|
const out = [];
|
|
60487
60476
|
for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));
|
|
60488
60477
|
return out;
|
|
@@ -60604,7 +60593,7 @@ var FaceRescoreJob = class {
|
|
|
60604
60593
|
fingerprint: fingerprint.combined
|
|
60605
60594
|
});
|
|
60606
60595
|
const paramsByDevice = /* @__PURE__ */ new Map();
|
|
60607
|
-
for (const batchIds of chunk
|
|
60596
|
+
for (const batchIds of chunk(ids, RESCORE_BATCH_SIZE)) {
|
|
60608
60597
|
if (this.stopped) throw new Error("stopped before completion");
|
|
60609
60598
|
const storeStart = this.ports.now();
|
|
60610
60599
|
const batch = await this.ports.loadRescoreBatch(batchIds);
|
|
@@ -98360,431 +98349,6 @@ var SweepCursorStore = class SweepCursorStore {
|
|
|
98360
98349
|
}
|
|
98361
98350
|
};
|
|
98362
98351
|
//#endregion
|
|
98363
|
-
//#region src/durable/lts-aggregator.ts
|
|
98364
|
-
/** Wall-clock bucket width. Five minutes, matching HA's statistics tier. */
|
|
98365
|
-
var LTS_BUCKET_MS = 5 * 6e4;
|
|
98366
|
-
/**
|
|
98367
|
-
* Row cap per (subject, series, scope) — one year of five-minute buckets, the
|
|
98368
|
-
* same number HA's own docs use. At ~60 bytes a row that is ~6 MB per series
|
|
98369
|
-
* per camera-year, against a `media` table already measured in hundreds of MB.
|
|
98370
|
-
*/
|
|
98371
|
-
var LTS_MAX_ROWS_PER_SERIES = 105120;
|
|
98372
|
-
/** Buckets written per transaction — bounds one statement, not the flush. */
|
|
98373
|
-
var FLUSH_BATCH = 200;
|
|
98374
|
-
/** How often the row cap is enforced. Rarely: it is a bound, not a deadline. */
|
|
98375
|
-
var CAP_SWEEP_INTERVAL_MS = 6 * 36e5;
|
|
98376
|
-
/**
|
|
98377
|
-
* The row's identity IS its four key columns (D871): a composite
|
|
98378
|
-
* `primaryKey` + `WITHOUT ROWID`, so the table is its own index and there is no
|
|
98379
|
-
* TEXT `id` repeating what the row already says. The old layout kept
|
|
98380
|
-
* `<subject>|<series>|<scope>|<bucketStart>` as a TEXT primary key beside the
|
|
98381
|
-
* four columns — 85 MB of rows plus a 44 MB autoindex for 1.07 M rows. A
|
|
98382
|
-
* composite-key collection is written with `upsertMany` (a bucket flushed twice
|
|
98383
|
-
* replaces itself) and read with `query` / `count` / `deleteWhere`; the calls
|
|
98384
|
-
* that address one row by id are refused by the engine.
|
|
98385
|
-
*/
|
|
98386
|
-
var LTS_KEY_COLUMNS = [
|
|
98387
|
-
"subject",
|
|
98388
|
-
"series",
|
|
98389
|
-
"scope",
|
|
98390
|
-
"bucketStart"
|
|
98391
|
-
];
|
|
98392
|
-
var LTS_COLUMNS = [
|
|
98393
|
-
(
|
|
98394
|
-
/** The camera id, or the node id. One column, because every query is
|
|
98395
|
-
* "this thing over time" and the thing is one or the other. */
|
|
98396
|
-
{
|
|
98397
|
-
name: "subject",
|
|
98398
|
-
type: "TEXT"
|
|
98399
|
-
}),
|
|
98400
|
-
(
|
|
98401
|
-
/** Numeric mirror of `subject` for camera rows, so a per-camera question is
|
|
98402
|
-
* answered by an integer index — every log line and every query about a
|
|
98403
|
-
* device in this repo is keyed by the numeric id. `NULL` for node rows. */
|
|
98404
|
-
{
|
|
98405
|
-
name: "deviceId",
|
|
98406
|
-
type: "INTEGER"
|
|
98407
|
-
}),
|
|
98408
|
-
{
|
|
98409
|
-
name: "series",
|
|
98410
|
-
type: "TEXT"
|
|
98411
|
-
},
|
|
98412
|
-
(
|
|
98413
|
-
/** Sub-scope within a series: a zoneId for occupancy, `''` otherwise. */
|
|
98414
|
-
{
|
|
98415
|
-
name: "scope",
|
|
98416
|
-
type: "TEXT"
|
|
98417
|
-
}),
|
|
98418
|
-
(
|
|
98419
|
-
/** Bucket start, wall-clock aligned to {@link LTS_BUCKET_MS}. */
|
|
98420
|
-
{
|
|
98421
|
-
name: "bucketStart",
|
|
98422
|
-
type: "INTEGER"
|
|
98423
|
-
}),
|
|
98424
|
-
{
|
|
98425
|
-
name: "samples",
|
|
98426
|
-
type: "INTEGER",
|
|
98427
|
-
notNull: true
|
|
98428
|
-
},
|
|
98429
|
-
{
|
|
98430
|
-
name: "sum",
|
|
98431
|
-
type: "REAL",
|
|
98432
|
-
notNull: true
|
|
98433
|
-
},
|
|
98434
|
-
{
|
|
98435
|
-
name: "min",
|
|
98436
|
-
type: "REAL",
|
|
98437
|
-
notNull: true
|
|
98438
|
-
},
|
|
98439
|
-
{
|
|
98440
|
-
name: "max",
|
|
98441
|
-
type: "REAL",
|
|
98442
|
-
notNull: true
|
|
98443
|
-
}
|
|
98444
|
-
];
|
|
98445
|
-
/**
|
|
98446
|
-
* The primary key serves every per-subject read (`subject, series, scope` then a
|
|
98447
|
-
* `bucketStart` range, already in bucket order); this index serves the read that
|
|
98448
|
-
* names a series without a subject. The old `deviceId` index went with the TEXT
|
|
98449
|
-
* id: nothing queries a series by the numeric mirror.
|
|
98450
|
-
*/
|
|
98451
|
-
var LTS_INDEXES = [{
|
|
98452
|
-
name: "idx_lts_series_bucket",
|
|
98453
|
-
columns: ["series", "bucketStart"]
|
|
98454
|
-
}];
|
|
98455
|
-
function ltsBucketStart(at, bucketMs = LTS_BUCKET_MS) {
|
|
98456
|
-
return Math.floor(at / bucketMs) * bucketMs;
|
|
98457
|
-
}
|
|
98458
|
-
var LtsAggregator = class {
|
|
98459
|
-
open = /* @__PURE__ */ new Map();
|
|
98460
|
-
/** Every (subject, series, scope) that has produced a row in this process —
|
|
98461
|
-
* the groups the cap sweep has any reason to look at. */
|
|
98462
|
-
groups = /* @__PURE__ */ new Set();
|
|
98463
|
-
lastCapSweepAt = 0;
|
|
98464
|
-
collection;
|
|
98465
|
-
store;
|
|
98466
|
-
logger;
|
|
98467
|
-
nowFn;
|
|
98468
|
-
bucketMs;
|
|
98469
|
-
maxRows;
|
|
98470
|
-
constructor(deps) {
|
|
98471
|
-
this.collection = deps.collection;
|
|
98472
|
-
this.store = deps.store;
|
|
98473
|
-
this.logger = deps.logger;
|
|
98474
|
-
this.nowFn = deps.now ?? (() => Date.now());
|
|
98475
|
-
this.bucketMs = deps.bucketMs ?? LTS_BUCKET_MS;
|
|
98476
|
-
this.maxRows = deps.maxRowsPerSeries ?? LTS_MAX_ROWS_PER_SERIES;
|
|
98477
|
-
}
|
|
98478
|
-
static declare(store, collection) {
|
|
98479
|
-
return store.declareCollection.mutate({
|
|
98480
|
-
collection,
|
|
98481
|
-
columns: [...LTS_COLUMNS],
|
|
98482
|
-
primaryKey: [...LTS_KEY_COLUMNS],
|
|
98483
|
-
withoutRowid: true,
|
|
98484
|
-
indexes: [...LTS_INDEXES]
|
|
98485
|
-
});
|
|
98486
|
-
}
|
|
98487
|
-
/**
|
|
98488
|
-
* Record one observation. Synchronous, allocation-free after the first
|
|
98489
|
-
* sample of a bucket, and it cannot throw — it sits on paths that are
|
|
98490
|
-
* already producing the value for another reason and must not learn a new
|
|
98491
|
-
* failure mode.
|
|
98492
|
-
*
|
|
98493
|
-
* A non-finite value is DROPPED rather than folded in: one `NaN` would make
|
|
98494
|
-
* `sum`, `min` and `max` all `NaN` for the whole bucket, turning a skewed
|
|
98495
|
-
* row into a meaningless one.
|
|
98496
|
-
*/
|
|
98497
|
-
note(input) {
|
|
98498
|
-
if (!Number.isFinite(input.value)) return;
|
|
98499
|
-
const at = input.at ?? this.nowFn();
|
|
98500
|
-
const scope = input.scope ?? "";
|
|
98501
|
-
const bucketStart = ltsBucketStart(at, this.bucketMs);
|
|
98502
|
-
const key = rowId(input.subject, input.series, scope, bucketStart);
|
|
98503
|
-
const held = this.open.get(key);
|
|
98504
|
-
if (held === void 0) {
|
|
98505
|
-
this.open.set(key, {
|
|
98506
|
-
subject: input.subject,
|
|
98507
|
-
...input.deviceId !== void 0 ? { deviceId: input.deviceId } : {},
|
|
98508
|
-
series: input.series,
|
|
98509
|
-
scope,
|
|
98510
|
-
bucketStart,
|
|
98511
|
-
samples: 1,
|
|
98512
|
-
sum: input.value,
|
|
98513
|
-
min: input.value,
|
|
98514
|
-
max: input.value
|
|
98515
|
-
});
|
|
98516
|
-
return;
|
|
98517
|
-
}
|
|
98518
|
-
held.samples += 1;
|
|
98519
|
-
held.sum += input.value;
|
|
98520
|
-
if (input.value < held.min) held.min = input.value;
|
|
98521
|
-
if (input.value > held.max) held.max = input.value;
|
|
98522
|
-
}
|
|
98523
|
-
/** Buckets currently accumulating — diagnostics, and what a flush would write. */
|
|
98524
|
-
openBuckets() {
|
|
98525
|
-
return [...this.open.values()].map(toRow);
|
|
98526
|
-
}
|
|
98527
|
-
/**
|
|
98528
|
-
* Write every bucket that has CLOSED (its window ended at or before `now`)
|
|
98529
|
-
* and drop it from RAM. Returns the number of rows written.
|
|
98530
|
-
*
|
|
98531
|
-
* The current bucket is deliberately left alone: writing it early would mean
|
|
98532
|
-
* rewriting it on the next tick, which turns one row into up to sixty and
|
|
98533
|
-
* offers every one of them to the checkpoint lottery (D96).
|
|
98534
|
-
*/
|
|
98535
|
-
async flushDue(now = this.nowFn()) {
|
|
98536
|
-
const currentBucket = ltsBucketStart(now, this.bucketMs);
|
|
98537
|
-
const due = [...this.open.values()].filter((b) => b.bucketStart < currentBucket);
|
|
98538
|
-
if (due.length === 0) {
|
|
98539
|
-
await this.maybeSweepCap(now);
|
|
98540
|
-
return 0;
|
|
98541
|
-
}
|
|
98542
|
-
let written = 0;
|
|
98543
|
-
for (const batch of chunk(due, FLUSH_BATCH)) {
|
|
98544
|
-
written += await this.writeBatch(batch);
|
|
98545
|
-
for (const bucket of batch) this.open.delete(rowId(bucket.subject, bucket.series, bucket.scope, bucket.bucketStart));
|
|
98546
|
-
}
|
|
98547
|
-
await this.maybeSweepCap(now);
|
|
98548
|
-
return written;
|
|
98549
|
-
}
|
|
98550
|
-
/**
|
|
98551
|
-
* Upsert one batch in a single transaction. If the batch is refused as a
|
|
98552
|
-
* whole (all-or-nothing), each bucket is retried alone so one bad row costs
|
|
98553
|
-
* one interval, not forty. Returns the number of rows written.
|
|
98554
|
-
*/
|
|
98555
|
-
async writeBatch(batch) {
|
|
98556
|
-
try {
|
|
98557
|
-
await this.upsert(batch);
|
|
98558
|
-
for (const bucket of batch) this.groups.add(groupKey(bucket.subject, bucket.series, bucket.scope));
|
|
98559
|
-
return batch.length;
|
|
98560
|
-
} catch (err) {
|
|
98561
|
-
if (batch.length > 1) {
|
|
98562
|
-
let written = 0;
|
|
98563
|
-
for (const bucket of batch) written += await this.writeBatch([bucket]);
|
|
98564
|
-
return written;
|
|
98565
|
-
}
|
|
98566
|
-
const bucket = batch[0];
|
|
98567
|
-
this.logger.warn("lts bucket write failed — this interval will be missing", {
|
|
98568
|
-
...bucket?.deviceId !== void 0 ? { tags: { deviceId: bucket.deviceId } } : {},
|
|
98569
|
-
meta: {
|
|
98570
|
-
collection: this.collection,
|
|
98571
|
-
series: bucket?.series,
|
|
98572
|
-
subject: bucket?.subject,
|
|
98573
|
-
bucketStart: bucket?.bucketStart,
|
|
98574
|
-
error: String(err)
|
|
98575
|
-
}
|
|
98576
|
-
});
|
|
98577
|
-
return 0;
|
|
98578
|
-
}
|
|
98579
|
-
}
|
|
98580
|
-
async upsert(batch) {
|
|
98581
|
-
await this.store.upsertMany.mutate({
|
|
98582
|
-
collection: this.collection,
|
|
98583
|
-
conflict: [...LTS_KEY_COLUMNS],
|
|
98584
|
-
records: batch.map((bucket) => ({ data: {
|
|
98585
|
-
subject: bucket.subject,
|
|
98586
|
-
deviceId: bucket.deviceId ?? null,
|
|
98587
|
-
series: bucket.series,
|
|
98588
|
-
scope: bucket.scope,
|
|
98589
|
-
bucketStart: bucket.bucketStart,
|
|
98590
|
-
samples: bucket.samples,
|
|
98591
|
-
sum: bucket.sum,
|
|
98592
|
-
min: bucket.min,
|
|
98593
|
-
max: bucket.max
|
|
98594
|
-
} }))
|
|
98595
|
-
});
|
|
98596
|
-
}
|
|
98597
|
-
/** Read closed buckets back. The read surface every chart will use. */
|
|
98598
|
-
async read(query = {}) {
|
|
98599
|
-
const where = {};
|
|
98600
|
-
if (query.series !== void 0) where["series"] = query.series;
|
|
98601
|
-
if (query.subject !== void 0) where["subject"] = query.subject;
|
|
98602
|
-
if (query.scope !== void 0) where["scope"] = query.scope;
|
|
98603
|
-
const records = await this.store.query.query({
|
|
98604
|
-
collection: this.collection,
|
|
98605
|
-
filter: {
|
|
98606
|
-
...Object.keys(where).length > 0 ? { where } : {},
|
|
98607
|
-
...query.from !== void 0 || query.to !== void 0 ? { whereBetween: { bucketStart: [query.from ?? 0, query.to ?? Number.MAX_SAFE_INTEGER] } } : {},
|
|
98608
|
-
orderBy: {
|
|
98609
|
-
field: "bucketStart",
|
|
98610
|
-
direction: "asc"
|
|
98611
|
-
},
|
|
98612
|
-
limit: query.limit ?? 5e3
|
|
98613
|
-
}
|
|
98614
|
-
});
|
|
98615
|
-
const rows = [];
|
|
98616
|
-
for (const record of records) {
|
|
98617
|
-
const row = recordToRow(record.data);
|
|
98618
|
-
if (row !== null) rows.push(row);
|
|
98619
|
-
}
|
|
98620
|
-
return rows;
|
|
98621
|
-
}
|
|
98622
|
-
/**
|
|
98623
|
-
* Enforce the per-(subject, series, scope) row cap, at most once every six
|
|
98624
|
-
* hours. Only groups this process has written to are examined: a group with
|
|
98625
|
-
* no new rows cannot have crossed a cap it was under.
|
|
98626
|
-
*/
|
|
98627
|
-
async maybeSweepCap(now) {
|
|
98628
|
-
if (now - this.lastCapSweepAt < CAP_SWEEP_INTERVAL_MS) return;
|
|
98629
|
-
this.lastCapSweepAt = now;
|
|
98630
|
-
for (const group of this.groups) {
|
|
98631
|
-
const [subject, series, scope] = group.split("\0");
|
|
98632
|
-
if (subject === void 0 || series === void 0 || scope === void 0) continue;
|
|
98633
|
-
const where = {
|
|
98634
|
-
subject,
|
|
98635
|
-
series,
|
|
98636
|
-
scope
|
|
98637
|
-
};
|
|
98638
|
-
try {
|
|
98639
|
-
const excess = await this.store.count.query({
|
|
98640
|
-
collection: this.collection,
|
|
98641
|
-
filter: { where }
|
|
98642
|
-
}) - this.maxRows;
|
|
98643
|
-
if (excess <= 0) continue;
|
|
98644
|
-
const cutoff = (await this.store.query.query({
|
|
98645
|
-
collection: this.collection,
|
|
98646
|
-
filter: {
|
|
98647
|
-
where,
|
|
98648
|
-
orderBy: {
|
|
98649
|
-
field: "bucketStart",
|
|
98650
|
-
direction: "asc"
|
|
98651
|
-
},
|
|
98652
|
-
limit: excess
|
|
98653
|
-
}
|
|
98654
|
-
})).at(-1)?.data["bucketStart"];
|
|
98655
|
-
if (typeof cutoff !== "number") continue;
|
|
98656
|
-
const { deleted } = await this.store.deleteWhere.mutate({
|
|
98657
|
-
collection: this.collection,
|
|
98658
|
-
filter: {
|
|
98659
|
-
where,
|
|
98660
|
-
whereBetween: { bucketStart: [0, cutoff] }
|
|
98661
|
-
}
|
|
98662
|
-
});
|
|
98663
|
-
this.logger.info("lts row cap enforced", { meta: {
|
|
98664
|
-
collection: this.collection,
|
|
98665
|
-
series,
|
|
98666
|
-
subject,
|
|
98667
|
-
deleted,
|
|
98668
|
-
cap: this.maxRows
|
|
98669
|
-
} });
|
|
98670
|
-
} catch (err) {
|
|
98671
|
-
this.logger.warn("lts row cap sweep failed — the series keeps growing this cycle", { meta: {
|
|
98672
|
-
collection: this.collection,
|
|
98673
|
-
series,
|
|
98674
|
-
subject,
|
|
98675
|
-
error: String(err)
|
|
98676
|
-
} });
|
|
98677
|
-
}
|
|
98678
|
-
}
|
|
98679
|
-
}
|
|
98680
|
-
};
|
|
98681
|
-
function chunk(items, size) {
|
|
98682
|
-
const out = [];
|
|
98683
|
-
for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));
|
|
98684
|
-
return out;
|
|
98685
|
-
}
|
|
98686
|
-
function groupKey(subject, series, scope) {
|
|
98687
|
-
return `${subject}${series}${scope}`;
|
|
98688
|
-
}
|
|
98689
|
-
function rowId(subject, series, scope, bucketStart) {
|
|
98690
|
-
return `${subject}|${series}|${scope}|${bucketStart}`;
|
|
98691
|
-
}
|
|
98692
|
-
function toRow(bucket) {
|
|
98693
|
-
return {
|
|
98694
|
-
subject: bucket.subject,
|
|
98695
|
-
...bucket.deviceId !== void 0 ? { deviceId: bucket.deviceId } : {},
|
|
98696
|
-
series: bucket.series,
|
|
98697
|
-
scope: bucket.scope,
|
|
98698
|
-
bucketStart: bucket.bucketStart,
|
|
98699
|
-
samples: bucket.samples,
|
|
98700
|
-
sum: bucket.sum,
|
|
98701
|
-
min: bucket.min,
|
|
98702
|
-
max: bucket.max
|
|
98703
|
-
};
|
|
98704
|
-
}
|
|
98705
|
-
/** Structural validation on read. A malformed row is skipped, never charted. */
|
|
98706
|
-
function recordToRow(data) {
|
|
98707
|
-
const subject = data["subject"];
|
|
98708
|
-
const series = data["series"];
|
|
98709
|
-
const scope = data["scope"];
|
|
98710
|
-
if (typeof subject !== "string" || typeof series !== "string") return null;
|
|
98711
|
-
const bucketStart = Number(data["bucketStart"]);
|
|
98712
|
-
const samples = Number(data["samples"]);
|
|
98713
|
-
const sum = Number(data["sum"]);
|
|
98714
|
-
const min = Number(data["min"]);
|
|
98715
|
-
const max = Number(data["max"]);
|
|
98716
|
-
if (![
|
|
98717
|
-
bucketStart,
|
|
98718
|
-
samples,
|
|
98719
|
-
sum,
|
|
98720
|
-
min,
|
|
98721
|
-
max
|
|
98722
|
-
].every((n) => Number.isFinite(n))) return null;
|
|
98723
|
-
const rawDeviceId = data["deviceId"];
|
|
98724
|
-
const deviceId = typeof rawDeviceId === "number" && Number.isFinite(rawDeviceId) ? rawDeviceId : void 0;
|
|
98725
|
-
return {
|
|
98726
|
-
subject,
|
|
98727
|
-
...deviceId !== void 0 ? { deviceId } : {},
|
|
98728
|
-
series,
|
|
98729
|
-
scope: typeof scope === "string" ? scope : "",
|
|
98730
|
-
bucketStart,
|
|
98731
|
-
samples,
|
|
98732
|
-
sum,
|
|
98733
|
-
min,
|
|
98734
|
-
max
|
|
98735
|
-
};
|
|
98736
|
-
}
|
|
98737
|
-
//#endregion
|
|
98738
|
-
//#region src/pipeline-analytics/store/lts-store.ts
|
|
98739
|
-
/**
|
|
98740
|
-
* @durable class=ledger owner=pipeline-analytics
|
|
98741
|
-
* write="one row per (camera, series, zone, 5-min bucket), written ONCE when the bucket closes; a camera with no sample writes nothing"
|
|
98742
|
-
* retention="row CAP per (subject, series, scope) — 105,120 rows ≈ one year of 5-minute buckets. Deliberately not an age sweep: an LTS row has no owner to be orphaned from and no clock but its own (D61)."
|
|
98743
|
-
*/
|
|
98744
|
-
var LTS_COLLECTION = "pipeline-analytics:stats-5m-v2";
|
|
98745
|
-
var AnalyticsLts = class {
|
|
98746
|
-
lts;
|
|
98747
|
-
constructor(deps) {
|
|
98748
|
-
this.lts = new LtsAggregator({
|
|
98749
|
-
collection: LTS_COLLECTION,
|
|
98750
|
-
store: deps.store,
|
|
98751
|
-
logger: deps.logger,
|
|
98752
|
-
...deps.now !== void 0 ? { now: deps.now } : {}
|
|
98753
|
-
});
|
|
98754
|
-
}
|
|
98755
|
-
static declare(store) {
|
|
98756
|
-
return LtsAggregator.declare(store, LTS_COLLECTION);
|
|
98757
|
-
}
|
|
98758
|
-
/**
|
|
98759
|
-
* One camera sample. Synchronous and infallible by contract — every call
|
|
98760
|
-
* site is a path that already produced the value for another reason, and
|
|
98761
|
-
* none of them may learn a new failure mode because a chart exists.
|
|
98762
|
-
*/
|
|
98763
|
-
note(series, deviceId, value, at, scope) {
|
|
98764
|
-
this.lts.note({
|
|
98765
|
-
subject: String(deviceId),
|
|
98766
|
-
deviceId,
|
|
98767
|
-
series,
|
|
98768
|
-
...scope !== void 0 ? { scope } : {},
|
|
98769
|
-
value,
|
|
98770
|
-
...at !== void 0 ? { at } : {}
|
|
98771
|
-
});
|
|
98772
|
-
}
|
|
98773
|
-
/** Write every bucket that has closed. Driven by the addon's flush timer. */
|
|
98774
|
-
flushDue(now) {
|
|
98775
|
-
return this.lts.flushDue(now);
|
|
98776
|
-
}
|
|
98777
|
-
/** The read surface charts will use. */
|
|
98778
|
-
read(query) {
|
|
98779
|
-
return this.lts.read(query);
|
|
98780
|
-
}
|
|
98781
|
-
/** Buckets still accumulating — makes "is it running" answerable at once,
|
|
98782
|
-
* instead of after five minutes of silence. */
|
|
98783
|
-
openBuckets() {
|
|
98784
|
-
return this.lts.openBuckets();
|
|
98785
|
-
}
|
|
98786
|
-
};
|
|
98787
|
-
//#endregion
|
|
98788
98352
|
//#region src/pipeline-analytics/store/identity-store.ts
|
|
98789
98353
|
/**
|
|
98790
98354
|
* IdentityStore — per-person identity registry for face recognition.
|
|
@@ -101828,13 +101392,6 @@ var ANALYTICS_COLLECTIONS = [
|
|
|
101828
101392
|
why: "a phantom cell is a property of the SCENE — \"this patch of pixels keeps producing 4-second motionless tracks\" — not of any track. The tracks that produced it are dead by construction (that is the entire premise: the stationary registry cannot see them because they never live long enough to be promoted), so there is no owner it could be orphaned from. Its bound is its own 24h close window plus a 512-cell-per-device cap, both enforced by the registry on write — deliberately not an age sweep, which would be a second clock (D61)."
|
|
101829
101393
|
}
|
|
101830
101394
|
},
|
|
101831
|
-
{
|
|
101832
|
-
collection: LTS_COLLECTION,
|
|
101833
|
-
classification: {
|
|
101834
|
-
kind: "never-orphaned",
|
|
101835
|
-
why: "a five-minute statistics row summarises an INTERVAL, not an entity — there is no owner it can be orphaned from. Its bound is a ROW CAP per (subject, series, scope) enforced by the aggregator itself (105,120 rows ≈ one year), deliberately not an age sweep: D61 collapsed ten disagreeing age clocks into one, and an LTS series has no clock but its own."
|
|
101836
|
-
}
|
|
101837
|
-
},
|
|
101838
101395
|
{
|
|
101839
101396
|
collection: RETRAIN_ANNOTATIONS_COLLECTION,
|
|
101840
101397
|
classification: {
|
|
@@ -107138,6 +106695,78 @@ Object.freeze({
|
|
|
107138
106695
|
"path": "online",
|
|
107139
106696
|
"valueType": "boolean"
|
|
107140
106697
|
}],
|
|
106698
|
+
"dishwasher": [
|
|
106699
|
+
{
|
|
106700
|
+
"path": "state",
|
|
106701
|
+
"valueType": "enum",
|
|
106702
|
+
"enumValues": [
|
|
106703
|
+
"idle",
|
|
106704
|
+
"running",
|
|
106705
|
+
"paused",
|
|
106706
|
+
"finished",
|
|
106707
|
+
"error",
|
|
106708
|
+
"unavailable"
|
|
106709
|
+
]
|
|
106710
|
+
},
|
|
106711
|
+
{
|
|
106712
|
+
"path": "program",
|
|
106713
|
+
"valueType": "string"
|
|
106714
|
+
},
|
|
106715
|
+
{
|
|
106716
|
+
"path": "remainingMinutes",
|
|
106717
|
+
"valueType": "number"
|
|
106718
|
+
},
|
|
106719
|
+
{
|
|
106720
|
+
"path": "progressPercent",
|
|
106721
|
+
"valueType": "number"
|
|
106722
|
+
},
|
|
106723
|
+
{
|
|
106724
|
+
"path": "doorOpen",
|
|
106725
|
+
"valueType": "boolean"
|
|
106726
|
+
},
|
|
106727
|
+
{
|
|
106728
|
+
"path": "remoteStartAllowed",
|
|
106729
|
+
"valueType": "boolean"
|
|
106730
|
+
},
|
|
106731
|
+
{
|
|
106732
|
+
"path": "powerOn",
|
|
106733
|
+
"valueType": "boolean"
|
|
106734
|
+
},
|
|
106735
|
+
{
|
|
106736
|
+
"path": "saltLevel",
|
|
106737
|
+
"valueType": "enum",
|
|
106738
|
+
"enumValues": [
|
|
106739
|
+
"ok",
|
|
106740
|
+
"low",
|
|
106741
|
+
"empty"
|
|
106742
|
+
]
|
|
106743
|
+
},
|
|
106744
|
+
{
|
|
106745
|
+
"path": "rinseAidLevel",
|
|
106746
|
+
"valueType": "enum",
|
|
106747
|
+
"enumValues": [
|
|
106748
|
+
"ok",
|
|
106749
|
+
"low",
|
|
106750
|
+
"empty"
|
|
106751
|
+
]
|
|
106752
|
+
},
|
|
106753
|
+
{
|
|
106754
|
+
"path": "halfLoad",
|
|
106755
|
+
"valueType": "boolean"
|
|
106756
|
+
},
|
|
106757
|
+
{
|
|
106758
|
+
"path": "hygienePlus",
|
|
106759
|
+
"valueType": "boolean"
|
|
106760
|
+
},
|
|
106761
|
+
{
|
|
106762
|
+
"path": "errorCode",
|
|
106763
|
+
"valueType": "number"
|
|
106764
|
+
},
|
|
106765
|
+
{
|
|
106766
|
+
"path": "errorLabel",
|
|
106767
|
+
"valueType": "string"
|
|
106768
|
+
}
|
|
106769
|
+
],
|
|
107141
106770
|
"doorbell": [{
|
|
107142
106771
|
"path": "lastPressedAt",
|
|
107143
106772
|
"valueType": "number"
|
|
@@ -107228,6 +106857,10 @@ Object.freeze({
|
|
|
107228
106857
|
"path": "detected",
|
|
107229
106858
|
"valueType": "boolean"
|
|
107230
106859
|
}],
|
|
106860
|
+
"heating-valve": [{
|
|
106861
|
+
"path": "position",
|
|
106862
|
+
"valueType": "number"
|
|
106863
|
+
}],
|
|
107231
106864
|
"humidifier": [
|
|
107232
106865
|
{
|
|
107233
106866
|
"path": "on",
|
|
@@ -110656,6 +110289,189 @@ var MigrationMarkerStore = class MigrationMarkerStore {
|
|
|
110656
110289
|
};
|
|
110657
110290
|
};
|
|
110658
110291
|
//#endregion
|
|
110292
|
+
//#region src/pipeline-analytics/store/analytics-history-keys.ts
|
|
110293
|
+
/** Declared addon series keys (D850). */
|
|
110294
|
+
var ANALYTICS_HISTORY_BUCKET_SERIES = {
|
|
110295
|
+
motion: "addon:pipeline-analytics/motion",
|
|
110296
|
+
"audio-dbfs": "addon:pipeline-analytics/audio-dbfs",
|
|
110297
|
+
occupancy: "addon:pipeline-analytics/occupancy"
|
|
110298
|
+
};
|
|
110299
|
+
var ANALYTICS_AUDIO_LEVEL_KEY = "addon:pipeline-analytics/audio-level";
|
|
110300
|
+
/** Map the public cap enum to the device-history key (read path). */
|
|
110301
|
+
function historyKeyForLtsSeries(series) {
|
|
110302
|
+
if (series === "battery") return "cap:battery.percentage";
|
|
110303
|
+
return ANALYTICS_HISTORY_BUCKET_SERIES[series];
|
|
110304
|
+
}
|
|
110305
|
+
//#endregion
|
|
110306
|
+
//#region src/pipeline-analytics/store/analytics-history-producer.ts
|
|
110307
|
+
var BUCKET_SERIES_META = [
|
|
110308
|
+
{
|
|
110309
|
+
name: "motion",
|
|
110310
|
+
displayName: "Motion events (region count)"
|
|
110311
|
+
},
|
|
110312
|
+
{
|
|
110313
|
+
name: "audio-dbfs",
|
|
110314
|
+
displayName: "Audio noise baseline (dBFS)",
|
|
110315
|
+
unit: "dBFS"
|
|
110316
|
+
},
|
|
110317
|
+
{
|
|
110318
|
+
name: "occupancy",
|
|
110319
|
+
displayName: "Occupancy count"
|
|
110320
|
+
}
|
|
110321
|
+
];
|
|
110322
|
+
var AUDIO_LEVEL_SERIES = {
|
|
110323
|
+
name: "audio-level",
|
|
110324
|
+
displayName: "Audio level (dBFS)",
|
|
110325
|
+
unit: "dBFS"
|
|
110326
|
+
};
|
|
110327
|
+
var AnalyticsHistoryProducer = class {
|
|
110328
|
+
accum = new require_clip_model_registry.BucketAccumulator();
|
|
110329
|
+
deviceHistory;
|
|
110330
|
+
logger;
|
|
110331
|
+
nowFn;
|
|
110332
|
+
declared = false;
|
|
110333
|
+
constructor(deps) {
|
|
110334
|
+
this.deviceHistory = deps.deviceHistory;
|
|
110335
|
+
this.logger = deps.logger;
|
|
110336
|
+
this.nowFn = deps.now ?? (() => Date.now());
|
|
110337
|
+
}
|
|
110338
|
+
async declare() {
|
|
110339
|
+
if (this.declared) return;
|
|
110340
|
+
for (const meta of BUCKET_SERIES_META) await this.deviceHistory.declareSeries({
|
|
110341
|
+
name: meta.name,
|
|
110342
|
+
subjectKind: "device",
|
|
110343
|
+
kind: "gauge",
|
|
110344
|
+
displayName: meta.displayName,
|
|
110345
|
+
ingest: "buckets",
|
|
110346
|
+
...meta.unit !== void 0 ? { unit: meta.unit } : {}
|
|
110347
|
+
});
|
|
110348
|
+
await this.deviceHistory.declareSeries({
|
|
110349
|
+
name: AUDIO_LEVEL_SERIES.name,
|
|
110350
|
+
subjectKind: "device",
|
|
110351
|
+
kind: "gauge",
|
|
110352
|
+
displayName: AUDIO_LEVEL_SERIES.displayName,
|
|
110353
|
+
unit: AUDIO_LEVEL_SERIES.unit,
|
|
110354
|
+
ingest: "points"
|
|
110355
|
+
});
|
|
110356
|
+
this.declared = true;
|
|
110357
|
+
}
|
|
110358
|
+
note(series, deviceId, value, at, scope) {
|
|
110359
|
+
const key = ANALYTICS_HISTORY_BUCKET_SERIES[series];
|
|
110360
|
+
this.accum.note(key, {
|
|
110361
|
+
kind: "device",
|
|
110362
|
+
deviceId
|
|
110363
|
+
}, scope ?? "", value, at ?? this.nowFn());
|
|
110364
|
+
}
|
|
110365
|
+
recordAudioLevel(deviceId, dbfs, at) {
|
|
110366
|
+
const record = this.deviceHistory.record;
|
|
110367
|
+
if (record === void 0) return;
|
|
110368
|
+
record.call(this.deviceHistory, { points: [{
|
|
110369
|
+
key: ANALYTICS_AUDIO_LEVEL_KEY,
|
|
110370
|
+
subject: {
|
|
110371
|
+
kind: "device",
|
|
110372
|
+
deviceId
|
|
110373
|
+
},
|
|
110374
|
+
scope: "",
|
|
110375
|
+
ts: at,
|
|
110376
|
+
value: dbfs
|
|
110377
|
+
}] }).catch((err) => {
|
|
110378
|
+
this.logger.warn("audio level history point not stored", {
|
|
110379
|
+
tags: { deviceId },
|
|
110380
|
+
meta: { error: err instanceof Error ? err.message : String(err) }
|
|
110381
|
+
});
|
|
110382
|
+
});
|
|
110383
|
+
}
|
|
110384
|
+
async flushDue(now) {
|
|
110385
|
+
const at = now ?? this.nowFn();
|
|
110386
|
+
const closed = this.accum.takeClosed(at);
|
|
110387
|
+
if (closed.length === 0) return 0;
|
|
110388
|
+
try {
|
|
110389
|
+
return (await this.deviceHistory.recordBuckets({ buckets: closed })).accepted;
|
|
110390
|
+
} catch (err) {
|
|
110391
|
+
const dropped = this.accum.requeue(closed);
|
|
110392
|
+
this.logger.warn("analytics history bucket flush failed — requeued", { meta: {
|
|
110393
|
+
buckets: closed.length,
|
|
110394
|
+
dropped: dropped.length,
|
|
110395
|
+
error: err instanceof Error ? err.message : String(err)
|
|
110396
|
+
} });
|
|
110397
|
+
return 0;
|
|
110398
|
+
}
|
|
110399
|
+
}
|
|
110400
|
+
pendingBucketCount() {
|
|
110401
|
+
return this.accum.pendingBucketCount();
|
|
110402
|
+
}
|
|
110403
|
+
async readLongTermBuckets(input) {
|
|
110404
|
+
const key = historyKeyForLtsSeries(input.series);
|
|
110405
|
+
const result = await this.deviceHistory.query({
|
|
110406
|
+
deviceId: input.deviceId,
|
|
110407
|
+
key,
|
|
110408
|
+
...input.scope !== void 0 ? { scope: input.scope } : {},
|
|
110409
|
+
from: input.from,
|
|
110410
|
+
to: input.to,
|
|
110411
|
+
resolution: "5m",
|
|
110412
|
+
...input.limit !== void 0 ? { maxPoints: input.limit } : {}
|
|
110413
|
+
});
|
|
110414
|
+
const out = [];
|
|
110415
|
+
for (const s of result.series) for (const p of s.points) {
|
|
110416
|
+
if (p.mean === null) continue;
|
|
110417
|
+
out.push({
|
|
110418
|
+
scope: s.scope,
|
|
110419
|
+
bucketStart: p.t,
|
|
110420
|
+
samples: p.samples,
|
|
110421
|
+
sum: p.mean * p.samples,
|
|
110422
|
+
min: p.min,
|
|
110423
|
+
max: p.max
|
|
110424
|
+
});
|
|
110425
|
+
}
|
|
110426
|
+
return out.toSorted((a, b) => a.bucketStart - b.bucketStart);
|
|
110427
|
+
}
|
|
110428
|
+
/** Admin debug read — maps device-history 5m buckets to legacy LTS row shape. */
|
|
110429
|
+
async readDebug(input) {
|
|
110430
|
+
const deviceId = input.subject !== void 0 ? Number(input.subject) : NaN;
|
|
110431
|
+
if (!Number.isFinite(deviceId) || input.series === void 0) return [];
|
|
110432
|
+
const key = historyKeyForLtsSeries(input.series);
|
|
110433
|
+
const result = await this.deviceHistory.query({
|
|
110434
|
+
deviceId,
|
|
110435
|
+
key,
|
|
110436
|
+
...input.scope !== void 0 ? { scope: input.scope } : {},
|
|
110437
|
+
from: input.from ?? 0,
|
|
110438
|
+
to: input.to ?? this.nowFn(),
|
|
110439
|
+
resolution: "5m",
|
|
110440
|
+
...input.limit !== void 0 ? { maxPoints: input.limit } : {}
|
|
110441
|
+
});
|
|
110442
|
+
const rows = [];
|
|
110443
|
+
for (const s of result.series) for (const p of s.points) {
|
|
110444
|
+
if (p.mean === null) continue;
|
|
110445
|
+
rows.push({
|
|
110446
|
+
subject: String(deviceId),
|
|
110447
|
+
deviceId,
|
|
110448
|
+
series: input.series,
|
|
110449
|
+
scope: s.scope,
|
|
110450
|
+
bucketStart: p.t,
|
|
110451
|
+
samples: p.samples,
|
|
110452
|
+
sum: p.mean * p.samples,
|
|
110453
|
+
min: p.min,
|
|
110454
|
+
max: p.max
|
|
110455
|
+
});
|
|
110456
|
+
}
|
|
110457
|
+
return rows;
|
|
110458
|
+
}
|
|
110459
|
+
openBucketsForDebug() {
|
|
110460
|
+
return [];
|
|
110461
|
+
}
|
|
110462
|
+
};
|
|
110463
|
+
//#endregion
|
|
110464
|
+
//#region src/pipeline-analytics/store/device-history-write-client.ts
|
|
110465
|
+
/** Adapt tRPC-shaped `ctx.api.deviceHistory` to the producer's plain async surface. */
|
|
110466
|
+
function deviceHistoryWriteClient(api) {
|
|
110467
|
+
return {
|
|
110468
|
+
declareSeries: (input) => api.declareSeries.mutate(input),
|
|
110469
|
+
record: (input) => api.record.mutate({ points: [...input.points] }),
|
|
110470
|
+
recordBuckets: (input) => api.recordBuckets.mutate({ buckets: [...input.buckets] }),
|
|
110471
|
+
query: (input) => api.query.query(input)
|
|
110472
|
+
};
|
|
110473
|
+
}
|
|
110474
|
+
//#endregion
|
|
110659
110475
|
//#region src/pipeline-analytics/store/media-subject-shot.ts
|
|
110660
110476
|
/** Preference among rows that tie on time. Lower is better. */
|
|
110661
110477
|
var SUBJECT_SHOT_KIND_RANK = {
|
|
@@ -112990,7 +112806,7 @@ var NcArtifactSecretSchema = require_clip_model_registry.string();
|
|
|
112990
112806
|
var EmbeddingEnabledSchema = require_clip_model_registry.boolean();
|
|
112991
112807
|
var SILENCE_FLOOR_DBFS = -55;
|
|
112992
112808
|
var RETENTION_SWEEP_INTERVAL_MS = 5 * 6e4;
|
|
112993
|
-
/** How often closed
|
|
112809
|
+
/** How often closed history buckets are flushed. See `analytics-history-producer.ts`. */
|
|
112994
112810
|
var LTS_FLUSH_INTERVAL_MS = 6e4;
|
|
112995
112811
|
/**
|
|
112996
112812
|
* How long the events ops-log is kept when the cluster has said nothing. 30
|
|
@@ -114646,7 +114462,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
114646
114462
|
/**
|
|
114647
114463
|
* Five-minute long-term statistics — the tier this system did not have.
|
|
114648
114464
|
* Global for every camera (no opt-in: a baseline nobody enables is the
|
|
114649
|
-
* failure mode `observe` mode was already in). See `
|
|
114465
|
+
* failure mode `observe` mode was already in). See `analytics-history-producer.ts`.
|
|
114650
114466
|
*/
|
|
114651
114467
|
lts = null;
|
|
114652
114468
|
/** The runner node that last produced a frame for a camera — see
|
|
@@ -114813,7 +114629,6 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
114813
114629
|
onAudioResult: (data) => this.handleAudioResult(data),
|
|
114814
114630
|
onMotionAnalysis: (deviceId, data, timestampMs) => this.handleMotionAnalysis(deviceId, data, timestampMs),
|
|
114815
114631
|
onOnboardMotion: (deviceId, data, timestampMs) => this.handleOnboardMotion(deviceId, data, timestampMs),
|
|
114816
|
-
onBatterySample: (deviceId, percentage, at) => this.lts?.note("battery", deviceId, percentage, at),
|
|
114817
114632
|
onSensorStateChanged: (data, timestampMs) => this.dispatchSensorStateChanged(data, timestampMs),
|
|
114818
114633
|
onBindingsChanged: (data) => this.handleBindingsChangedEvent(data),
|
|
114819
114634
|
onDeviceUnregistered: (deviceId) => this.handleDeviceUnregisteredEvent(deviceId),
|
|
@@ -115142,7 +114957,6 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
115142
114957
|
TrackingTapeIndexStore.declare(api.settingsStore),
|
|
115143
114958
|
BirthDecisionLedgerStore.declare(api.settingsStore),
|
|
115144
114959
|
TrackRelationsStore.declare(api.settingsStore),
|
|
115145
|
-
AnalyticsLts.declare(api.settingsStore),
|
|
115146
114960
|
declarePaAnalyticsBootRecordBackedLegacy(api.settingsStore, isHub),
|
|
115147
114961
|
declareNotificationCenterBootCollections(api.settingsStore, isHub)
|
|
115148
114962
|
]);
|
|
@@ -115498,9 +115312,12 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
115498
115312
|
}
|
|
115499
115313
|
});
|
|
115500
115314
|
this.refreshDeviceAlertSettings();
|
|
115501
|
-
this.lts = new
|
|
115502
|
-
|
|
115503
|
-
logger: logger.child("
|
|
115315
|
+
this.lts = new AnalyticsHistoryProducer({
|
|
115316
|
+
deviceHistory: deviceHistoryWriteClient(api.deviceHistory),
|
|
115317
|
+
logger: logger.child("device-history")
|
|
115318
|
+
});
|
|
115319
|
+
this.lts.declare().catch((err) => {
|
|
115320
|
+
logger.warn("pipeline-analytics history series declare failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
115504
115321
|
});
|
|
115505
115322
|
const lts = this.lts;
|
|
115506
115323
|
const eventStore = new EventStore({
|
|
@@ -116967,8 +116784,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
116967
116784
|
open: []
|
|
116968
116785
|
};
|
|
116969
116786
|
return {
|
|
116970
|
-
rows: (await lts.
|
|
116971
|
-
open: lts.
|
|
116787
|
+
rows: (await lts.readDebug(input)).map(withMean),
|
|
116788
|
+
open: lts.openBucketsForDebug().map(withMean)
|
|
116972
116789
|
};
|
|
116973
116790
|
},
|
|
116974
116791
|
/**
|
|
@@ -121044,33 +120861,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
121044
120861
|
});
|
|
121045
120862
|
this.levelStateByDevice.set(deviceId, res.nextState);
|
|
121046
120863
|
if (!res.emit) return;
|
|
121047
|
-
const
|
|
121048
|
-
this.
|
|
121049
|
-
className: void 0,
|
|
121050
|
-
atMs: timestamp
|
|
121051
|
-
});
|
|
121052
|
-
const insertedId = await this.eventStore.insertAudio(draft);
|
|
121053
|
-
if (insertedId === null) return;
|
|
121054
|
-
const ev = {
|
|
121055
|
-
...draft,
|
|
121056
|
-
id: insertedId
|
|
121057
|
-
};
|
|
121058
|
-
this.ctx.eventBus.emit({
|
|
121059
|
-
id: `pa-${ev.id}`,
|
|
121060
|
-
timestamp: new Date(ev.timestamp),
|
|
121061
|
-
source: {
|
|
121062
|
-
type: "addon",
|
|
121063
|
-
id: "pipeline-analytics",
|
|
121064
|
-
addonId: "pipeline-analytics"
|
|
121065
|
-
},
|
|
121066
|
-
category: require_clip_model_registry.EventCategory.PipelineAnalyticsDetectionEvent,
|
|
121067
|
-
data: {
|
|
121068
|
-
deviceId,
|
|
121069
|
-
kind: "audio",
|
|
121070
|
-
eventId: ev.id,
|
|
121071
|
-
timestamp: ev.timestamp
|
|
121072
|
-
}
|
|
121073
|
-
});
|
|
120864
|
+
const dbfs = level?.dbfs ?? Number.NEGATIVE_INFINITY;
|
|
120865
|
+
this.lts?.recordAudioLevel(deviceId, dbfs, timestamp);
|
|
121074
120866
|
}
|
|
121075
120867
|
/**
|
|
121076
120868
|
* Persist motion-analysis bus events into the EPISODE tracker (D475): a
|
|
@@ -125185,21 +124977,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
125185
124977
|
async readLongTermSeries(input) {
|
|
125186
124978
|
const lts = this.lts;
|
|
125187
124979
|
if (!lts) throw new Error("pipeline-analytics: the long-term store is not ready yet");
|
|
125188
|
-
return
|
|
125189
|
-
subject: String(input.deviceId),
|
|
125190
|
-
series: input.series,
|
|
125191
|
-
...input.scope !== void 0 ? { scope: input.scope } : {},
|
|
125192
|
-
from: input.from,
|
|
125193
|
-
to: input.to,
|
|
125194
|
-
...input.limit !== void 0 ? { limit: input.limit } : {}
|
|
125195
|
-
})).map((r) => ({
|
|
125196
|
-
scope: r.scope,
|
|
125197
|
-
bucketStart: r.bucketStart,
|
|
125198
|
-
samples: r.samples,
|
|
125199
|
-
sum: r.sum,
|
|
125200
|
-
min: r.min,
|
|
125201
|
-
max: r.max
|
|
125202
|
-
}));
|
|
124980
|
+
return lts.readLongTermBuckets(input);
|
|
125203
124981
|
}
|
|
125204
124982
|
async getEventStoreFootprint() {
|
|
125205
124983
|
return this.queryFacade.getEventStoreFootprint();
|