@camstack/addon-post-analysis 1.2.622 → 1.2.623
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/THIRD_PARTY_MODELS.md +2 -2
- package/dist/{clip-model-registry-EDTOCKN9.mjs → clip-model-registry-C58fyELd.mjs} +181 -65
- package/dist/{clip-model-registry-MHJ9BSUV.js → clip-model-registry-KKT83z9U.js} +192 -64
- 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-DAyVukQp.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-_ahOxwNA.mjs} +3 -3
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-V5-s2hmZ.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-CssYIiFD.mjs → _virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-B1HZu0i3.mjs} +1 -1
- package/dist/pipeline-analytics/{hostInit-C4-dCKYR.mjs → hostInit-CdRVx4vF.mjs} +3 -3
- package/dist/pipeline-analytics/index.js +335 -86
- package/dist/pipeline-analytics/index.mjs +335 -86
- 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-CTvx3PDS.mjs +0 -26
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as MediaFileKindEnum, $i as
|
|
1
|
+
import { $ as MediaFileKindEnum, $i as defineCustomActions, $n as cosineSimilarity$1, $r as plateGalleryCapability, $t as OpsLogEntrySchema, A as DeclaredDevices, Ai as systemEventFamilyProblems, An as activateAlarmProfile, Ar as isEventKindLabelKey, At as NcAlarmSensorsInputSchema, B as FULL_IMAGE_BBOX, Bi as DEFAULT_FIRST_SIGHTING_FRESHNESS_MS, Bn as applySecureOnArmEdit, Br as isVectorIndexDimConflict, Bt as NcRuleUpdateInputSchema, C as ClearTrackRelationResultSchema, Ca as EventCategory, Ci as storageOccupancyCapability, Cn as TimelapseRulePatchSchema, Cr as isAlertEpisodeOpen, Ct as NC_SNOOZE_MAX_MINUTES, D as DEFAULT_TIMELAPSE_PREVIEW_TEXT, Di as summarizeReview, Dn as TrackRelationSchema, Dr as isDefaultAlarmProfileId, Dt as NC_TEMPLATE_VARS, E as DEFAULT_EVENT_COLOR, Ei as subjectPathSvgPoints, En as TrackRelationKindSchema, Er as isAudioClassName, Et as NC_TAXONOMY, F as EVENT_DENSITY_BATCH_MAX, Fi as templateVarsFor, Fn as alertEpisodeKind, Fr as isRetiredClearKey, Ft as NcRuleActionSchema, G as ListTrackRelationsResultSchema, Gi as errMsg, Gn as buildEventKindDescriptor, Gr as ncMediaPolicyError, Gt as NcSnoozeSuppressedSchema, H as LABEL_SUPPLY_MACRO, Hi as evaluateSensorEdge, Hn as assertTimelapseCadences, Hr as linkedStepOf, Ht as NcSharedSequenceSchema, I as EVENT_KIND_BY_CAP, Ii as vectorDimFromBase64, In as alertEpisodePayload, Ir as isRootSuppliableStepId, It as NcRuleInputSchema, J as MAX_BIRTH_DECISION_RECORDS, Ji as InvalidRecordError, Jn as calendarEventLocalSpan, Jr as occupancyScope, Jt as NcTaxonomySchema, K as LockStateSchema, Ki as BaseAddon, Kn as buildSubjectPath, Kr as normalizeNcMediaPolicy, Kt as NcStateSummarySectionIdSchema, L as EVENT_OWNER_TYPES, Li as viewerLinkBaseOf, Ln as applyBlockingEdit, Lr as isScheduleActive, Lt as NcRulePatchSchema, M as EMBEDDING_MODEL_FAILED, Mi as systemEventSummaryScheduleOf, Mn as alarmDeviceSecureOnArm, Mr as isKnownTemplateVar, Mt as NcAlarmSettingsPatchSchema, N as EMBEDDING_REQUIREMENTS_FAILED, Ni as systemEventSummarySplitOf, Nn as alarmPanelCapability, Nr as isLabelSupplyStepId, Nt as NcConditionDescriptorSchema, O as DETECTION_MACRO_CLASSES, Oi as syncActiveAlarmProfileSettings, On as TrackSideSchema, Or as isDetectionMacroClass, Ot as NcAlarmConfigSchema, P as EMBEDDING_REQUIREMENTS_UNAVAILABLE, Pi as templateCarriesSummary, Pn as alertEpisodeId, Pr as isMediaOwnerType, Pt as NcConditionsSchema, Q as MOTION_CLOSE_AFTER_MS, Qi as customAction, Qn as coreBlockIdFromAddonId, Qr as pipelineAnalyticsCapability, Qt as OPS_LOG_DEFAULT_LIMIT, R as EXPORT_DENSE_MAX_RANGES, Ri as withSystemEventClasses, Rn as applyModeDelaysEdits, Rr as isSystemDelivery, Rt as NcRuleSchema, S as ClearTrackRelationInputSchema, Sa as unknown, Si as startMoverUnderClearance, Sn as TimelapseRuleInputSchema, Sr as isAlarmControllableCap, St as NC_SENSOR_TRANSITION_KINDS, T as DEFAULT_DETAIL_CROP_CONVENTION, Ti as subjectPathSampleFromBox, Tn as TrackPuritySchema, Tr as isAppLinkBase, Tt as NC_TANK_SYSTEM_EVENT_KINDS, U as LabelAttributionSchema, Ui as isSourceCap, Un as audioMetricsCapability, Ur as mayWriteToLocation, Ut as NcSnoozeInputSchema, V as FailureCounters, Vi as canonicalHash, Vn as armRemovalReconcile, Vr as kebabToCamel, Vt as NcScheduleSchema, W as ListTrackRelationsInputSchema, Wi as DeviceType, Wn as audioModeOf, Wr as memoizePinnedFfmpeg, Wt as NcSnoozeSchema, X as MAX_TRACK_PURITIES, Xi as RECORD_QUERY_MAX, Xn as cloneAlarmSettings, Xt as NcTemplatePreviewSchema, Y as MAX_BIRTH_LATENCY_PROXY_MS, Yi as RECORD_MAX_DATA_BYTES, Yn as classifyRetrainReview, Yr as parseAlertEpisodePayload, Yt as NcTemplatePreviewInputSchema, Z as MAX_TRACK_RELATIONS, Zi as createEvent, Zn as consumableLowPercentOf, Zr as pickFaceRescoreIntervalMinutes, Zt as NcTemplateVarDescriptorSchema, _ as BIRTH_DECISION_DEFAULT_LIMIT, _a as strictObject, _i as ruleKindOf, _n as SetTrackPurityInputSchema, _r as evictionPolicyOfLocation, _t as NC_CONSUMABLE_SYSTEM_EVENT_KINDS, a as CLIP_EMBEDDING_STEP_ID, aa as sleep$1, ai as resolveClipSearchMinScore, an as RetrainOriginSchema, ar as describeSystemEventDeliveryProblem, at as NC_ALARM_NOTIFY_FIELDS, b as BucketAccumulator, ba as tuple, bi as sensorRaisedOf, bn as TIMELAPSE_DENSE_FLOOR_SEC, br as gifDeriveSpeed, bt as NC_GROUPING_MAX_SEC, c as readClusterModelPinStrict, ca as array, ci as resolveModeNotifyPolicy, cn as SCENE_CONFIRM_DEFAULT_TIMEOUT_MS, cr as deviceRemovalHookCapability, ct as NC_AUDIO_CONFIRM_HITS_DEFAULT, d as APPEARANCE_BLOCKS, da as literal, di as retrainRevisionConflictMessage, dn as SCENE_DEFAULT_OBSERVATION_SPACING_SEC, dr as embeddingRefusalOf, dt as NC_CLIP_PRE_ROLL_DEFAULT_SEC, ea as defineRecordKinds, ei as projectSubjectPath, er as createAlarmProfile, et as MotionTriggeredBySchema, f as APPEARANCE_RESOLVES_AMBIGUITY_DEFAULT, fa as looseObject, fi as rootSupplyServes, fn as SCENE_DEFAULT_QUIET_SECONDS, ft as NC_CLIP_RETENTION_MAX_SEC, g as ArchivedDebugNoteSchema, ga as record, gi as ruleGroupingApplies, gn as SceneMonitorSchema, gr as eventKindLabel, gt as NC_CONSUMABLE_LOW_PERCENT_DEFAULT, h as AlarmArmModeSchema, ha as partialRecord, hi as ruleDeviceCompatCapQueries, hn as SCENE_RESET_RECAPTURES, hr as ensureAlarmProfiles, ht as NC_CONFIRM_DEFAULT_TIMEOUT_MS, ii as renameAlarmProfile, in as RetrainDismissedSchema, ir as deriveRecordingMode, it as NC_ALARM_DELAY_MAX_SEC, j as DetectionSourceSchema, ji as systemEventFilterApplies, jn as addonWidgetsSourceCapability, jr as isEventOwnerType, jt as NcAlarmSensorsSchema, k as DETECTION_PIPELINE_CAP_NAME, ki as systemEventDeliveryProblems, kn as TrackSourceSchema, kr as isDrawableSubjectPath, kt as NcAlarmPostTriggeredActionSchema, l as resolveClusterModelPin, la as boolean, li as resolveNcMediaImage, ln as SCENE_DEFAULT_ANCHOR_THRESHOLD, lt as NC_AUDIO_CONFIRM_WINDOW_SEC_DEFAULT, m as AUDIO_MACRO_LABELS, ma as object, mi as ruleBelongsToAlarm$1, mn as SCENE_DIVERGED, mr as encodeVectorBase64, mt as NC_CONFIRM_DEFAULT_MAX_IMAGE_PX, na as isDeviceScopedCap, ni as readMotionTriggeredBy, nn as RECORDING_SOURCE_CAMSTACK_ADDON, nr as deriveDetailCropRect, nt as NC_ALARM_DEFAULT_TRIGGERED_DURATION_SEC, o as CLUSTER_MODEL_OWNER_ADDON_ID, oa as ZodObject, oi as resolveLocationMode, on as RetrainStatusSchema, or as describeSystemEventFamilyProblem, ot as NC_ALARM_SYSTEM_EVENT_KINDS, p as ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT, pa as number, pi as ruleAutoTriggersAlarm, pn as SCENE_DEFAULT_UNCOVERED_POLICY, pt as NC_CONDITION_CATALOG, q as MAX_ARCHIVED_DEBUG_NOTES, qi as CamProfileSchema, qn as buildViewerLink, qr as notificationRulesCapability, qt as NcSystemEventKindSchema, ra as nodePin, ri as readTimelapseGeneratedAt, rn as RebuildObjectEmbeddingsRefusalSchema, rr as derivePostTriggeredAction, rt as NC_ALARM_DELAY_FIELDS, s as FACE_EMBEDDING_STEP_ID, sa as _enum, si as resolveModeDelays, sn as SCENE_CONFIRM_DEFAULT_MAX_IMAGE_PX, sr as deviceAttentionKind, st as NC_ALARM_TRIGGERED_MAX_SEC, t as BUILTIN_CLIP_MODELS, ta as hydrateSchema, ti as readDeviceStateFrom, tn as RECORDING_EXPORT_MAX_READ_BYTES, tr as deleteAlarmProfile, tt as NC_ALARM_DEFAULT_PROFILE_ID, u as APPEARANCE_BIN_COUNT, ua as discriminatedUnion, un as SCENE_DEFAULT_CHECK_INTERVAL_SEC, ut as NC_CLIP_POST_ROLL_DEFAULT_SEC, v as BaseDevice, va as string, vi as sceneMonitorCapability, vn as SetTrackPurityResultSchema, vr as faceGalleryCapability, vt as NC_DEFAULT_SNOOZE_MINUTES, w as CoverStateSchema, wi as subKindsOf, wn as TimelapseRuleSchema, wr as isAlertEpisodeSeverity, wt as NC_SYSTEM_EVENT_KIND_GROUP_SPECS, x as COCO_TO_MACRO, xa as union, xi as servedTemplateVars, xn as TRACK_RELATIONS_DEFAULT_LIMIT, xr as gifFromMp4Args, xt as NC_GROUPING_MIN_SEC, y as BirthDecisionRecordSchema, ya as templateLiteral, yi as secureActuationFor, yn as SetTrackRelationInputSchema, yr as failureContributionCapability, yt as NC_GROUPING_DEFAULT_SEC, z as FIRST_LEVEL_MACRO_CLASSES, zi as zoneAnalyticsCapability, zn as applyNonBlockingEdit, zr as isSystemEventSummaryRule, zt as NcRuleTargetSchema } from "../clip-model-registry-C58fyELd.mjs";
|
|
2
2
|
import { t as __exportAll } from "../embedding-encoder/index.mjs";
|
|
3
3
|
import * as fs$1 from "node:fs";
|
|
4
4
|
import { createWriteStream, promises } from "node:fs";
|
|
@@ -5406,41 +5406,106 @@ var NC_TRACKS_AT_INSTANT_LIMIT = 25;
|
|
|
5406
5406
|
*/
|
|
5407
5407
|
var NC_TRACK_LIFE_SLACK_MS = 200;
|
|
5408
5408
|
/**
|
|
5409
|
+
* A position this close to the instant is drawn as it is: one detection tick,
|
|
5410
|
+
* the same notion as {@link NC_TRACK_LIFE_SLACK_MS}. Anything farther is a
|
|
5411
|
+
* position of a MOVED subject, so it is either interpolated (a position on
|
|
5412
|
+
* each side inside the tolerance) or the track is left out — never drawn at a
|
|
5413
|
+
* later place than it was at the picture's instant.
|
|
5414
|
+
*/
|
|
5415
|
+
var NC_BOX_EXACT_MS = NC_TRACK_LIFE_SLACK_MS;
|
|
5416
|
+
/** The box at `timestamp`: a near position as is, else the bracketing pair interpolated. */
|
|
5417
|
+
function boxAt(positions, timestamp, toleranceMs) {
|
|
5418
|
+
let nearest = null;
|
|
5419
|
+
let before = null;
|
|
5420
|
+
let after = null;
|
|
5421
|
+
for (const p of positions) {
|
|
5422
|
+
const d = p.timestamp - timestamp;
|
|
5423
|
+
if (nearest === null || Math.abs(d) < Math.abs(nearest.timestamp - timestamp)) nearest = p;
|
|
5424
|
+
if (d <= 0 && (before === null || p.timestamp > before.timestamp)) before = p;
|
|
5425
|
+
if (d >= 0 && (after === null || p.timestamp < after.timestamp)) after = p;
|
|
5426
|
+
}
|
|
5427
|
+
if (nearest === null) return null;
|
|
5428
|
+
const dt = nearest.timestamp - timestamp;
|
|
5429
|
+
if (Math.abs(dt) <= NC_BOX_EXACT_MS) return {
|
|
5430
|
+
bbox: nearest.bbox,
|
|
5431
|
+
score: nearest.score,
|
|
5432
|
+
dtMs: dt,
|
|
5433
|
+
interpolated: false
|
|
5434
|
+
};
|
|
5435
|
+
if (before === null || after === null || timestamp - before.timestamp > toleranceMs || after.timestamp - timestamp > toleranceMs) return null;
|
|
5436
|
+
const span = after.timestamp - before.timestamp;
|
|
5437
|
+
const f = span === 0 ? 0 : (timestamp - before.timestamp) / span;
|
|
5438
|
+
const lerp = (a, b) => a + (b - a) * f;
|
|
5439
|
+
return {
|
|
5440
|
+
bbox: {
|
|
5441
|
+
x: lerp(before.bbox.x, after.bbox.x),
|
|
5442
|
+
y: lerp(before.bbox.y, after.bbox.y),
|
|
5443
|
+
w: lerp(before.bbox.w, after.bbox.w),
|
|
5444
|
+
h: lerp(before.bbox.h, after.bbox.h)
|
|
5445
|
+
},
|
|
5446
|
+
score: nearest.score,
|
|
5447
|
+
dtMs: dt,
|
|
5448
|
+
interpolated: true
|
|
5449
|
+
};
|
|
5450
|
+
}
|
|
5451
|
+
/**
|
|
5409
5452
|
* Every source track ALIVE at `timestamp` (± {@link NC_TRACK_LIFE_SLACK_MS})
|
|
5410
|
-
* with a
|
|
5411
|
-
*
|
|
5412
|
-
*
|
|
5453
|
+
* with a box AT that instant: a trail position within {@link NC_BOX_EXACT_MS},
|
|
5454
|
+
* else the linear interpolation of the positions bracketing it (each within
|
|
5455
|
+
* `toleranceMs`). A track with neither is not drawn — `onDrop` names it.
|
|
5456
|
+
* Deduplicated by track id (a live copy and a persisted copy are one track).
|
|
5457
|
+
* Pure.
|
|
5413
5458
|
*/
|
|
5414
|
-
function tracksAtInstant(sources, timestamp, toleranceMs, fallbackDims) {
|
|
5459
|
+
function tracksAtInstant(sources, timestamp, toleranceMs, fallbackDims, onDrop) {
|
|
5415
5460
|
const out = /* @__PURE__ */ new Map();
|
|
5416
5461
|
for (const track of sources) {
|
|
5417
5462
|
if (out.has(track.trackId)) continue;
|
|
5418
|
-
if (track.firstSeen > timestamp + NC_TRACK_LIFE_SLACK_MS || track.lastSeen < timestamp - NC_TRACK_LIFE_SLACK_MS)
|
|
5463
|
+
if (track.firstSeen > timestamp + NC_TRACK_LIFE_SLACK_MS || track.lastSeen < timestamp - NC_TRACK_LIFE_SLACK_MS) {
|
|
5464
|
+
onDrop?.({
|
|
5465
|
+
trackId: track.trackId,
|
|
5466
|
+
className: track.className,
|
|
5467
|
+
reason: "outside-life"
|
|
5468
|
+
});
|
|
5469
|
+
continue;
|
|
5470
|
+
}
|
|
5419
5471
|
const w = track.frameWidth ?? fallbackDims?.w ?? 0;
|
|
5420
5472
|
const h = track.frameHeight ?? fallbackDims?.h ?? 0;
|
|
5421
|
-
if (w <= 0 || h <= 0)
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5473
|
+
if (w <= 0 || h <= 0) {
|
|
5474
|
+
onDrop?.({
|
|
5475
|
+
trackId: track.trackId,
|
|
5476
|
+
className: track.className,
|
|
5477
|
+
reason: "no-frame-dims"
|
|
5478
|
+
});
|
|
5479
|
+
continue;
|
|
5480
|
+
}
|
|
5481
|
+
const at = boxAt(track.positions, timestamp, toleranceMs);
|
|
5482
|
+
if (at === null) {
|
|
5483
|
+
const nearestDtMs = track.positions.reduce((best, p) => {
|
|
5484
|
+
const d = p.timestamp - timestamp;
|
|
5485
|
+
return best === void 0 || Math.abs(d) < Math.abs(best) ? d : best;
|
|
5486
|
+
}, void 0);
|
|
5487
|
+
onDrop?.({
|
|
5488
|
+
trackId: track.trackId,
|
|
5489
|
+
className: track.className,
|
|
5490
|
+
reason: "no-position-near-instant",
|
|
5491
|
+
...nearestDtMs !== void 0 ? { nearestDtMs } : {}
|
|
5492
|
+
});
|
|
5493
|
+
continue;
|
|
5430
5494
|
}
|
|
5431
|
-
if (best === null || bestDelta > toleranceMs) continue;
|
|
5432
5495
|
const label = track.subLabel ?? track.label;
|
|
5433
5496
|
out.set(track.trackId, {
|
|
5434
5497
|
trackId: track.trackId,
|
|
5435
5498
|
className: track.className,
|
|
5436
5499
|
...label !== void 0 && label !== track.className ? { label } : {},
|
|
5437
|
-
...
|
|
5500
|
+
...at.score !== void 0 ? { confidence: at.score } : {},
|
|
5438
5501
|
bbox: {
|
|
5439
|
-
x:
|
|
5440
|
-
y:
|
|
5441
|
-
w:
|
|
5442
|
-
h:
|
|
5443
|
-
}
|
|
5502
|
+
x: at.bbox.x / w,
|
|
5503
|
+
y: at.bbox.y / h,
|
|
5504
|
+
w: at.bbox.w / w,
|
|
5505
|
+
h: at.bbox.h / h
|
|
5506
|
+
},
|
|
5507
|
+
dtMs: at.dtMs,
|
|
5508
|
+
interpolated: at.interpolated
|
|
5444
5509
|
});
|
|
5445
5510
|
}
|
|
5446
5511
|
return [...out.values()];
|
|
@@ -19342,11 +19407,16 @@ var DebugNoteArchiveStore = class {
|
|
|
19342
19407
|
};
|
|
19343
19408
|
//#endregion
|
|
19344
19409
|
//#region src/pipeline-analytics/store/event-ledger-collection.ts
|
|
19410
|
+
/**
|
|
19411
|
+
* One event ledger (motion, audio, sensor, faces, plates …) over its
|
|
19412
|
+
* `ctx.records` kind. Every node has `ctx.records`, so there is no other path.
|
|
19413
|
+
*/
|
|
19345
19414
|
/** Matches {@link EventStore.LEDGER_PAGE_SIZE} — kept local to avoid a circular import. */
|
|
19346
19415
|
var LEDGER_PAGE_SIZE = 500;
|
|
19347
19416
|
var EventLedgerCollection = class EventLedgerCollection {
|
|
19348
19417
|
collection;
|
|
19349
19418
|
kind;
|
|
19419
|
+
records;
|
|
19350
19420
|
autoIntegerId;
|
|
19351
19421
|
legacyAtField;
|
|
19352
19422
|
deviceIdMode;
|
|
@@ -19362,6 +19432,7 @@ var EventLedgerCollection = class EventLedgerCollection {
|
|
|
19362
19432
|
this.logger = deps.logger;
|
|
19363
19433
|
this.deriveBody = deps.deriveBody;
|
|
19364
19434
|
this.indexFields = deps.definition.declaration.indexes.map((i) => i.fields);
|
|
19435
|
+
this.records = deps.records;
|
|
19365
19436
|
const handle = deps.records.kind(deps.definition);
|
|
19366
19437
|
this.kind = handle;
|
|
19367
19438
|
}
|
|
@@ -19380,6 +19451,31 @@ var EventLedgerCollection = class EventLedgerCollection {
|
|
|
19380
19451
|
}
|
|
19381
19452
|
await this.kind.put(put);
|
|
19382
19453
|
}
|
|
19454
|
+
/**
|
|
19455
|
+
* `upsertRow` as an op of {@link EventLedgerCollection.transact}. A row that
|
|
19456
|
+
* cannot be mapped THROWS here: inside a transaction a silently skipped write
|
|
19457
|
+
* would let the rest of the call commit without it.
|
|
19458
|
+
*/
|
|
19459
|
+
async txUpsert(id, value) {
|
|
19460
|
+
const put = this.buildRecordPut(id, value);
|
|
19461
|
+
if (put === null) throw new InvalidRecordError(`${this.collection}/${id}: the record could not be mapped`);
|
|
19462
|
+
return this.kind.txPut(put);
|
|
19463
|
+
}
|
|
19464
|
+
/** `update` as an op of {@link EventLedgerCollection.transact}. */
|
|
19465
|
+
txUpdate(id, patch) {
|
|
19466
|
+
return this.kind.txPatch(id, this.derived(patch, "patch"));
|
|
19467
|
+
}
|
|
19468
|
+
/** `deleteKey` as an op of {@link EventLedgerCollection.transact}. */
|
|
19469
|
+
txDelete(id) {
|
|
19470
|
+
return this.kind.txDelete(id);
|
|
19471
|
+
}
|
|
19472
|
+
/**
|
|
19473
|
+
* Run ops (built by the `tx*` methods of ledgers whose kinds share a
|
|
19474
|
+
* `transactFamily`) in ONE transaction: all land or none does.
|
|
19475
|
+
*/
|
|
19476
|
+
transact(ops) {
|
|
19477
|
+
return this.records.transact(ops);
|
|
19478
|
+
}
|
|
19383
19479
|
async insert(record) {
|
|
19384
19480
|
const idNum = this.autoIntegerId ? await this.kind.allocateId() : null;
|
|
19385
19481
|
const id = idNum !== null ? String(idNum) : typeof record["id"] === "string" ? record["id"] : null;
|
|
@@ -22321,8 +22417,9 @@ var RetrainAnnotationStore = class {
|
|
|
22321
22417
|
* The old rows' deletion, the new rows, the frame's `dismissed` set and its
|
|
22322
22418
|
* bumped `revision` run under a per-frame commit lock, guarded by
|
|
22323
22419
|
* `commit.fromRevision`: a concurrent save in this process leaves one winner
|
|
22324
|
-
* and one `conflict` — never a blend. The
|
|
22325
|
-
*
|
|
22420
|
+
* and one `conflict` — never a blend. The deletion, the new rows and the frame
|
|
22421
|
+
* row are ONE records `transact` (the four retrain kinds share a family): a
|
|
22422
|
+
* failure leaves the frame exactly as it was.
|
|
22326
22423
|
*
|
|
22327
22424
|
* Returns the persisted rows so the caller renders ids it can address.
|
|
22328
22425
|
*/
|
|
@@ -22439,21 +22536,14 @@ var RetrainAnnotationStore = class {
|
|
|
22439
22536
|
}
|
|
22440
22537
|
return stamped;
|
|
22441
22538
|
}
|
|
22442
|
-
/**
|
|
22443
|
-
|
|
22444
|
-
|
|
22445
|
-
|
|
22446
|
-
|
|
22447
|
-
|
|
22448
|
-
|
|
22449
|
-
|
|
22450
|
-
meta: {
|
|
22451
|
-
annotationId: row.id,
|
|
22452
|
-
error: String(err)
|
|
22453
|
-
}
|
|
22454
|
-
});
|
|
22455
|
-
}
|
|
22456
|
-
return rows.length;
|
|
22539
|
+
/**
|
|
22540
|
+
* The delete ops of every annotation of a frame, for ONE transaction with
|
|
22541
|
+
* the frame row and its memberships (RetrainService.removeFrame). A failed
|
|
22542
|
+
* read THROWS: "no annotations" must never be the answer to an unreadable
|
|
22543
|
+
* store, because the frame row would then be deleted over live boxes.
|
|
22544
|
+
*/
|
|
22545
|
+
async deleteOpsForFrame(mediaKey) {
|
|
22546
|
+
return (await this.annotations.query({ where: { mediaKey } })).map((row) => this.annotations.txDelete(row.id));
|
|
22457
22547
|
}
|
|
22458
22548
|
async replaceForFrameOnRecords(mediaKey, rows, commit, nextRevision) {
|
|
22459
22549
|
const frame = await this.frames.get(mediaKey);
|
|
@@ -22466,12 +22556,14 @@ var RetrainAnnotationStore = class {
|
|
|
22466
22556
|
kind: "conflict",
|
|
22467
22557
|
currentRevision
|
|
22468
22558
|
};
|
|
22469
|
-
|
|
22470
|
-
for (const
|
|
22471
|
-
await this.
|
|
22559
|
+
const ops = [];
|
|
22560
|
+
for (const existing of await this.annotations.query({ where: { mediaKey } })) ops.push(this.annotations.txDelete(existing.id));
|
|
22561
|
+
for (const row of rows) ops.push(await this.annotations.txUpsert(row.id, this.toRecord(row)));
|
|
22562
|
+
ops.push(await this.frames.txUpdate(mediaKey, {
|
|
22472
22563
|
revision: nextRevision,
|
|
22473
22564
|
...commit.dismissed !== void 0 ? { dismissed: [...commit.dismissed] } : {}
|
|
22474
|
-
});
|
|
22565
|
+
}));
|
|
22566
|
+
await this.frames.transact(ops);
|
|
22475
22567
|
return {
|
|
22476
22568
|
kind: "saved",
|
|
22477
22569
|
rows,
|
|
@@ -22489,8 +22581,10 @@ var RetrainAnnotationStore = class {
|
|
|
22489
22581
|
kind: "conflict",
|
|
22490
22582
|
currentRevision
|
|
22491
22583
|
};
|
|
22492
|
-
|
|
22493
|
-
await this.
|
|
22584
|
+
const ops = [];
|
|
22585
|
+
for (const row of eligible) ops.push(await this.annotations.txUpdate(row.id, { origin: origins.get(row.id) ?? null }));
|
|
22586
|
+
ops.push(await this.frames.txUpdate(mediaKey, { revision: nextRevision }));
|
|
22587
|
+
await this.frames.transact(ops);
|
|
22494
22588
|
return {
|
|
22495
22589
|
kind: "updated",
|
|
22496
22590
|
updated: eligible.length,
|
|
@@ -22661,16 +22755,16 @@ var RetrainCollectionStore = class {
|
|
|
22661
22755
|
}, counts.get(input.id) ?? 0)
|
|
22662
22756
|
};
|
|
22663
22757
|
}
|
|
22664
|
-
/** Remove the collection and its memberships
|
|
22665
|
-
*
|
|
22758
|
+
/** Remove the collection and its memberships in ONE transaction: a failure
|
|
22759
|
+
* leaves the collection with all its members, never memberships pointing at
|
|
22760
|
+
* a name that no longer exists, nor a collection that silently lost some. */
|
|
22666
22761
|
async delete(id) {
|
|
22667
22762
|
if (await this.readRow(id) === null) return {
|
|
22668
22763
|
deleted: false,
|
|
22669
22764
|
memberFrameIds: []
|
|
22670
22765
|
};
|
|
22671
22766
|
const members = await this.memberships.query({ where: { collectionId: id } });
|
|
22672
|
-
|
|
22673
|
-
await this.collections.deleteKey(id);
|
|
22767
|
+
await this.collections.transact([...members.map((m) => this.memberships.txDelete(m.id)), this.collections.txDelete(id)]);
|
|
22674
22768
|
this.logger.info("retrain collection deleted", { meta: {
|
|
22675
22769
|
collectionId: id,
|
|
22676
22770
|
members: members.length
|
|
@@ -22699,11 +22793,29 @@ var RetrainCollectionStore = class {
|
|
|
22699
22793
|
await this.memberships.deleteKey(key);
|
|
22700
22794
|
return true;
|
|
22701
22795
|
}
|
|
22702
|
-
/**
|
|
22703
|
-
|
|
22704
|
-
|
|
22705
|
-
|
|
22706
|
-
|
|
22796
|
+
/**
|
|
22797
|
+
* The delete ops of every membership of a frame, for ONE transaction with
|
|
22798
|
+
* the frame row and its annotations (RetrainService.removeFrame). A failed
|
|
22799
|
+
* read THROWS: the frame row must not be deleted over memberships nobody
|
|
22800
|
+
* could list.
|
|
22801
|
+
*/
|
|
22802
|
+
async forgetFrameOps(frameId) {
|
|
22803
|
+
return (await this.memberships.query({ where: { frameId } })).map((r) => this.memberships.txDelete(r.id));
|
|
22804
|
+
}
|
|
22805
|
+
/**
|
|
22806
|
+
* The op that adds a frame to a collection, for ONE transaction with the
|
|
22807
|
+
* frame's own row (a still stored from the recording). `null` when it is
|
|
22808
|
+
* already a member: there is nothing to write.
|
|
22809
|
+
*/
|
|
22810
|
+
async addFrameOp(input) {
|
|
22811
|
+
const key = membershipKey(input.collectionId, input.frameId);
|
|
22812
|
+
if (await this.memberships.get(key) !== null) return null;
|
|
22813
|
+
return this.memberships.txUpsert(key, {
|
|
22814
|
+
collectionId: input.collectionId,
|
|
22815
|
+
frameId: input.frameId,
|
|
22816
|
+
deviceId: input.deviceId,
|
|
22817
|
+
addedAt: this.now()
|
|
22818
|
+
});
|
|
22707
22819
|
}
|
|
22708
22820
|
/** The frames of a collection, in the order they were added. */
|
|
22709
22821
|
async listFrameIds(collectionId) {
|
|
@@ -23295,7 +23407,7 @@ var RetrainFrameStore = class {
|
|
|
23295
23407
|
* returned untouched; the blob lands before the row; a refusal is named.
|
|
23296
23408
|
* `capturedAt` IS the instant — the camera saw this frame then.
|
|
23297
23409
|
*/
|
|
23298
|
-
async storeRecordingStill(input) {
|
|
23410
|
+
async storeRecordingStill(input, withRow = []) {
|
|
23299
23411
|
const frameId = recordingFrameId(input.deviceId, input.epochMs);
|
|
23300
23412
|
const existing = await this.get(frameId);
|
|
23301
23413
|
if (existing !== null) return {
|
|
@@ -23342,7 +23454,7 @@ var RetrainFrameStore = class {
|
|
|
23342
23454
|
relativePath: path,
|
|
23343
23455
|
data: input.bytes
|
|
23344
23456
|
});
|
|
23345
|
-
await this.ledger.
|
|
23457
|
+
const row = await this.ledger.txUpsert(frameId, {
|
|
23346
23458
|
deviceId: frame.deviceId,
|
|
23347
23459
|
trackId: "",
|
|
23348
23460
|
sourceMediaId: 0,
|
|
@@ -23359,6 +23471,7 @@ var RetrainFrameStore = class {
|
|
|
23359
23471
|
origin: "recording",
|
|
23360
23472
|
epochMs: input.epochMs
|
|
23361
23473
|
});
|
|
23474
|
+
await this.ledger.transact([row, ...withRow]);
|
|
23362
23475
|
};
|
|
23363
23476
|
try {
|
|
23364
23477
|
if (this.writeGate) await this.writeGate.run(persist);
|
|
@@ -23412,22 +23525,33 @@ var RetrainFrameStore = class {
|
|
|
23412
23525
|
/**
|
|
23413
23526
|
* Drop a copy and its blob — the operator un-selecting a frame.
|
|
23414
23527
|
*
|
|
23415
|
-
* The
|
|
23416
|
-
*
|
|
23417
|
-
*
|
|
23528
|
+
* The ROW goes first, in one transaction with `withRow` (the frame's
|
|
23529
|
+
* annotations and collection memberships, which the caller builds): a failure
|
|
23530
|
+
* leaves the frame, its boxes and its memberships all in place, and throws.
|
|
23531
|
+
* The blob goes after: if that fails the row is already gone and the bytes
|
|
23532
|
+
* are reclaimable junk, logged, never a row pointing at missing media.
|
|
23418
23533
|
*/
|
|
23419
|
-
async deleteFrame(frame) {
|
|
23534
|
+
async deleteFrame(frame, withRow = []) {
|
|
23420
23535
|
const remove = async () => {
|
|
23536
|
+
try {
|
|
23537
|
+
await this.ledger.transact([...withRow, this.ledger.txDelete(frame.frameId)]);
|
|
23538
|
+
} catch (err) {
|
|
23539
|
+
this.logger.error("retrain frame delete failed; nothing was removed", {
|
|
23540
|
+
tags: { deviceId: frame.deviceId },
|
|
23541
|
+
meta: {
|
|
23542
|
+
frameId: frame.frameId,
|
|
23543
|
+
error: String(err)
|
|
23544
|
+
}
|
|
23545
|
+
});
|
|
23546
|
+
throw err;
|
|
23547
|
+
}
|
|
23421
23548
|
try {
|
|
23422
23549
|
await this.storage.delete({
|
|
23423
23550
|
location: frame.locationId ?? LEGACY_RETRAIN_MEDIA_LOCATION,
|
|
23424
23551
|
relativePath: framePath(frame)
|
|
23425
23552
|
});
|
|
23426
|
-
} catch {}
|
|
23427
|
-
try {
|
|
23428
|
-
await this.ledger.deleteKey(frame.frameId);
|
|
23429
23553
|
} catch (err) {
|
|
23430
|
-
this.logger.warn("
|
|
23554
|
+
this.logger.warn("retrain frame removed but its blob could not be deleted", {
|
|
23431
23555
|
tags: { deviceId: frame.deviceId },
|
|
23432
23556
|
meta: {
|
|
23433
23557
|
frameId: frame.frameId,
|
|
@@ -28405,7 +28529,9 @@ function incomingFromSystemEvent(event, lookup) {
|
|
|
28405
28529
|
const blindForMs = numberField(data, "blindForMs");
|
|
28406
28530
|
if (deviceId === void 0 || blindForMs === void 0) return null;
|
|
28407
28531
|
const identity = lookup(deviceId);
|
|
28408
|
-
const
|
|
28532
|
+
const motionMs = numberField(data, "motionMs") ?? blindForMs;
|
|
28533
|
+
const sessions = numberField(data, "sessions") ?? 1;
|
|
28534
|
+
const minutes = Math.max(1, Math.round(motionMs / 6e4));
|
|
28409
28535
|
return systemSubject({
|
|
28410
28536
|
facts,
|
|
28411
28537
|
kind: "detection-blind",
|
|
@@ -28414,13 +28540,39 @@ function incomingFromSystemEvent(event, lookup) {
|
|
|
28414
28540
|
bodyKey: "system.detection-blind.body",
|
|
28415
28541
|
textParams: {
|
|
28416
28542
|
...deviceTextParams(deviceId, identity),
|
|
28417
|
-
minutes: String(minutes)
|
|
28543
|
+
minutes: String(minutes),
|
|
28544
|
+
sessions: String(sessions)
|
|
28418
28545
|
},
|
|
28419
28546
|
textCount: minutes,
|
|
28420
28547
|
deviceId,
|
|
28421
28548
|
...identity !== void 0 ? { deviceType: identity.type } : {}
|
|
28422
28549
|
});
|
|
28423
28550
|
}
|
|
28551
|
+
case "pipeline.motion-noisy":
|
|
28552
|
+
case "pipeline.motion-calm": {
|
|
28553
|
+
const deviceId = numberField(data, "deviceId");
|
|
28554
|
+
const dutyPct = numberField(data, "dutyPct");
|
|
28555
|
+
const sessions = numberField(data, "sessions");
|
|
28556
|
+
const windowMs = numberField(data, "windowMs");
|
|
28557
|
+
if (deviceId === void 0 || dutyPct === void 0 || sessions === void 0 || windowMs === void 0) return null;
|
|
28558
|
+
const noisy = event.category === "pipeline.motion-noisy";
|
|
28559
|
+
const identity = lookup(deviceId);
|
|
28560
|
+
return systemSubject({
|
|
28561
|
+
facts,
|
|
28562
|
+
kind: noisy ? "motion-noisy" : "motion-calm",
|
|
28563
|
+
subject: `motion-noise:${deviceId}`,
|
|
28564
|
+
titleKey: noisy ? "system.motion-noisy.title" : "system.motion-calm.title",
|
|
28565
|
+
bodyKey: noisy ? "system.motion-noisy.body" : "system.motion-calm.body",
|
|
28566
|
+
textParams: {
|
|
28567
|
+
...deviceTextParams(deviceId, identity),
|
|
28568
|
+
minutes: String(Math.max(1, Math.round(windowMs / 6e4))),
|
|
28569
|
+
duty: String(dutyPct),
|
|
28570
|
+
sessions: String(sessions)
|
|
28571
|
+
},
|
|
28572
|
+
deviceId,
|
|
28573
|
+
...identity !== void 0 ? { deviceType: identity.type } : {}
|
|
28574
|
+
});
|
|
28575
|
+
}
|
|
28424
28576
|
case "recording.health.stalled": {
|
|
28425
28577
|
const deviceId = numberField(data, "deviceId");
|
|
28426
28578
|
const idleMs = numberField(data, "idleMs");
|
|
@@ -28538,7 +28690,7 @@ function incomingFromSystemEvent(event, lookup) {
|
|
|
28538
28690
|
case "system.restarting": return incomingFromNodeRestarting(facts, data);
|
|
28539
28691
|
case "recording.export.completed": return incomingFromExportCompleted(facts, data);
|
|
28540
28692
|
case "backup.run-completed": return incomingFromBackupCompleted(facts, data);
|
|
28541
|
-
case "alert.episode": return incomingFromAlertEpisode(facts);
|
|
28693
|
+
case "alert.episode": return incomingFromAlertEpisode(facts, lookup);
|
|
28542
28694
|
default: return null;
|
|
28543
28695
|
}
|
|
28544
28696
|
}
|
|
@@ -28554,9 +28706,11 @@ function incomingFromSystemEvent(event, lookup) {
|
|
|
28554
28706
|
* Whether an edge is notified at all (once per episode, reconciled from the
|
|
28555
28707
|
* alerts store) is decided before this point, by `NcAlertEpisodeLedger`.
|
|
28556
28708
|
*/
|
|
28557
|
-
function incomingFromAlertEpisode(facts) {
|
|
28709
|
+
function incomingFromAlertEpisode(facts, lookup) {
|
|
28558
28710
|
const payload = parseAlertEpisodePayload(facts.data);
|
|
28559
28711
|
if (payload === null) return null;
|
|
28712
|
+
const attention = incomingFromDeviceAttention(facts, payload, lookup);
|
|
28713
|
+
if (attention !== null) return attention;
|
|
28560
28714
|
const kind = alertEpisodeKind(payload.severity, payload.phase);
|
|
28561
28715
|
if (kind === null) return null;
|
|
28562
28716
|
return systemSubject({
|
|
@@ -28577,6 +28731,38 @@ function incomingFromAlertEpisode(facts) {
|
|
|
28577
28731
|
...payload.nodeId !== void 0 ? { nodeId: payload.nodeId } : {}
|
|
28578
28732
|
});
|
|
28579
28733
|
}
|
|
28734
|
+
/**
|
|
28735
|
+
* A CRITICAL alert whose source is a DEVICE (D911) is announced in the devices
|
|
28736
|
+
* family — `device-attention-required` / `-resolved`, carrying the device id so
|
|
28737
|
+
* a rule's device filters apply — and NOT as `alert-critical`, so one alert
|
|
28738
|
+
* never notifies twice through two families. A device source whose id is not a
|
|
28739
|
+
* device id falls through to the generic alert kinds.
|
|
28740
|
+
*/
|
|
28741
|
+
function incomingFromDeviceAttention(facts, payload, lookup) {
|
|
28742
|
+
if (payload.sourceType !== "device" || payload.sourceId === void 0) return null;
|
|
28743
|
+
const kind = deviceAttentionKind(payload.severity, payload.phase);
|
|
28744
|
+
if (kind === null) return null;
|
|
28745
|
+
const deviceId = Number(payload.sourceId);
|
|
28746
|
+
if (!Number.isInteger(deviceId)) return null;
|
|
28747
|
+
const identity = lookup?.(deviceId);
|
|
28748
|
+
return systemSubject({
|
|
28749
|
+
facts,
|
|
28750
|
+
kind,
|
|
28751
|
+
subject: `alert:${payload.episodeId}`,
|
|
28752
|
+
titleKey: `system.${kind}.title`,
|
|
28753
|
+
bodyKey: `system.${kind}.body`,
|
|
28754
|
+
textParams: {
|
|
28755
|
+
...deviceTextParams(deviceId, identity),
|
|
28756
|
+
title: payload.title,
|
|
28757
|
+
message: payload.message,
|
|
28758
|
+
category: payload.category,
|
|
28759
|
+
actionUrl: payload.actionUrl ?? ""
|
|
28760
|
+
},
|
|
28761
|
+
deviceId,
|
|
28762
|
+
...identity !== void 0 ? { deviceType: identity.type } : {},
|
|
28763
|
+
...payload.nodeId !== void 0 ? { nodeId: payload.nodeId } : {}
|
|
28764
|
+
});
|
|
28765
|
+
}
|
|
28580
28766
|
/** The shells whose update has its own sentence. Anything else gets the plain
|
|
28581
28767
|
* one: a body that names a shell nobody observed is worse than a vague body. */
|
|
28582
28768
|
var WRAPPER_BODY_KINDS = new Set([
|
|
@@ -30567,9 +30753,13 @@ var en_default = {
|
|
|
30567
30753
|
"system.addon-crash-loop.body": "The {{addons}} addon on node {{nodeId}} crashed {{count}} times in a row and will NOT be restarted. Everything it provides is offline until it is repaired.",
|
|
30568
30754
|
"system.detection-blind.title": "Detecting nothing — {{deviceName}}",
|
|
30569
30755
|
"system.detection-blind.body": {
|
|
30570
|
-
"one": "Camera {{deviceName}}
|
|
30571
|
-
"other": "Camera {{deviceName}}
|
|
30756
|
+
"one": "Camera {{deviceName}} moved for {{minutes}} minute over {{sessions}} sessions and the detector produced nothing at all, not even a weak candidate",
|
|
30757
|
+
"other": "Camera {{deviceName}} moved for {{minutes}} minutes over {{sessions}} sessions and the detector produced nothing at all, not even a weak candidate"
|
|
30572
30758
|
},
|
|
30759
|
+
"system.motion-noisy.title": "Constant motion, no subject — {{deviceName}}",
|
|
30760
|
+
"system.motion-noisy.body": "Camera {{deviceName}} has been in motion {{duty}}% of the last {{minutes}} minutes ({{sessions}} sessions) without a single detection. The detector is fine — tune this camera's motion zones or sensitivity.",
|
|
30761
|
+
"system.motion-calm.title": "Motion calm again — {{deviceName}}",
|
|
30762
|
+
"system.motion-calm.body": "Camera {{deviceName}} is no longer in constant motion ({{duty}}% of the last {{minutes}} minutes, {{sessions}} sessions)",
|
|
30573
30763
|
"system.recording-stalled.title": "Recording stalled — {{deviceName}}",
|
|
30574
30764
|
"system.recording-stalled.body": {
|
|
30575
30765
|
"one": "Camera {{deviceName}} has not written a recording segment for {{minutes}} minute",
|
|
@@ -30579,6 +30769,10 @@ var en_default = {
|
|
|
30579
30769
|
"system.recording-healthy.body": "Camera {{deviceName}} is writing recording segments again",
|
|
30580
30770
|
"system.recording-degraded.title": "Recording degraded — {{deviceName}}",
|
|
30581
30771
|
"system.recording-degraded.body": "Camera {{deviceName}} exhausted its writer restart budget ({{message}})",
|
|
30772
|
+
"system.device-attention-required.title": "{{title}}",
|
|
30773
|
+
"system.device-attention-required.body": "{{message}}",
|
|
30774
|
+
"system.device-attention-resolved.title": "Resolved: {{title}}",
|
|
30775
|
+
"system.device-attention-resolved.body": "{{deviceName}} no longer needs attention",
|
|
30582
30776
|
"system.storage-location-low.title": "Storage low — {{locationName}}",
|
|
30583
30777
|
"system.storage-location-low.body": "Location {{locationName}} is down to {{freePercent}}% free ({{reason}})",
|
|
30584
30778
|
"system.storage-location-critical.title": "Storage critical — {{locationName}}",
|
|
@@ -30967,6 +31161,10 @@ var it_default = {
|
|
|
30967
31161
|
"system.recording-healthy.body": "La camera {{deviceName}} scrive di nuovo i segmenti",
|
|
30968
31162
|
"system.recording-degraded.title": "Registrazione degradata — {{deviceName}}",
|
|
30969
31163
|
"system.recording-degraded.body": "La camera {{deviceName}} ha esaurito i riavvii del writer ({{message}})",
|
|
31164
|
+
"system.device-attention-required.title": "{{title}}",
|
|
31165
|
+
"system.device-attention-required.body": "{{message}}",
|
|
31166
|
+
"system.device-attention-resolved.title": "Risolto: {{title}}",
|
|
31167
|
+
"system.device-attention-resolved.body": "{{deviceName}} non richiede più intervento",
|
|
30970
31168
|
"system.storage-location-low.title": "Storage basso — {{locationName}}",
|
|
30971
31169
|
"system.storage-location-low.body": "La location {{locationName}} è al {{freePercent}}% libero ({{reason}})",
|
|
30972
31170
|
"system.storage-location-critical.title": "Storage critico — {{locationName}}",
|
|
@@ -30975,9 +31173,13 @@ var it_default = {
|
|
|
30975
31173
|
"system.storage-location-ok.body": "La location {{locationName}} ha recuperato spazio libero",
|
|
30976
31174
|
"system.detection-blind.title": "Senza detection — {{deviceName}}",
|
|
30977
31175
|
"system.detection-blind.body": {
|
|
30978
|
-
"one": "La camera {{deviceName}}
|
|
30979
|
-
"other": "La camera {{deviceName}}
|
|
31176
|
+
"one": "La camera {{deviceName}} ha avuto movimento per {{minutes}} minuto in {{sessions}} sessioni e il detector non ha prodotto nulla, nemmeno un candidato debole",
|
|
31177
|
+
"other": "La camera {{deviceName}} ha avuto movimento per {{minutes}} minuti in {{sessions}} sessioni e il detector non ha prodotto nulla, nemmeno un candidato debole"
|
|
30980
31178
|
},
|
|
31179
|
+
"system.motion-noisy.title": "Movimento continuo senza soggetto — {{deviceName}}",
|
|
31180
|
+
"system.motion-noisy.body": "La camera {{deviceName}} è stata in movimento il {{duty}}% degli ultimi {{minutes}} minuti ({{sessions}} sessioni) senza alcun rilevamento. Il detector funziona — regola le zone di movimento o la sensibilità di questa camera.",
|
|
31181
|
+
"system.motion-calm.title": "Movimento di nuovo calmo — {{deviceName}}",
|
|
31182
|
+
"system.motion-calm.body": "La camera {{deviceName}} non è più in movimento continuo ({{duty}}% degli ultimi {{minutes}} minuti, {{sessions}} sessioni)",
|
|
30981
31183
|
"system.addon-update-available.title": {
|
|
30982
31184
|
"one": "{{count}} aggiornamento addon disponibile",
|
|
30983
31185
|
"other": "{{count}} aggiornamenti addon disponibili"
|
|
@@ -33537,6 +33739,7 @@ function systemEventSubject(kind, scope) {
|
|
|
33537
33739
|
if (kind === "node-online" || kind === "node-offline") return `node:${scope.nodeId ?? "hub"}`;
|
|
33538
33740
|
if (kind === "node-inference-unavailable") return `node-inference:${scope.nodeId ?? "hub"}`;
|
|
33539
33741
|
if (kind === "detection-blind") return `detection-blind:${scope.deviceId ?? 0}`;
|
|
33742
|
+
if (kind === "motion-noisy" || kind === "motion-calm") return `motion-noise:${scope.deviceId ?? 0}`;
|
|
33540
33743
|
if (kind === "addon-crash-loop") return `addon-crash:${scope.nodeId ?? "hub"}:${TEST_CRASHED_RUNNER}`;
|
|
33541
33744
|
if (kind === "addon-update-available" || kind === "server-update-available") return `package:${scope.packageName ?? "@camstack/server"}`;
|
|
33542
33745
|
if (kind === "wrapper-update-available") return `wrapper:${scope.nodeId ?? "hub"}`;
|
|
@@ -33682,6 +33885,8 @@ function planRuleTest(input) {
|
|
|
33682
33885
|
seconds: "30",
|
|
33683
33886
|
devices: "cuda:0",
|
|
33684
33887
|
minutes: "15",
|
|
33888
|
+
duty: "55",
|
|
33889
|
+
sessions: "96",
|
|
33685
33890
|
addons: TEST_CRASHED_RUNNER,
|
|
33686
33891
|
list: "@camstack/addon-pipeline 1.0.1 (current 1.0.0)",
|
|
33687
33892
|
nodes: "hub",
|
|
@@ -40324,7 +40529,17 @@ var NcNotificationImageStage = class {
|
|
|
40324
40529
|
...subjectBox,
|
|
40325
40530
|
label: imageLabelText(payload.subject.label ?? payload.subject.className, payload.subject.confidence)
|
|
40326
40531
|
} : null;
|
|
40327
|
-
const
|
|
40532
|
+
const othersDrawn = mode === "scene-boxes" ? otherTracks(await this.tracksAt(camera, source.timestamp), subjectTrackId, subjectBox, payload.subject.className) : [];
|
|
40533
|
+
if (mode === "scene-boxes") this.deps.logger.info("scene-boxes: other boxes drawn", {
|
|
40534
|
+
tags: { deviceId: camera },
|
|
40535
|
+
meta: {
|
|
40536
|
+
ruleId: entry.ruleId,
|
|
40537
|
+
recordId: entry.recordId,
|
|
40538
|
+
instant: source.timestamp,
|
|
40539
|
+
drawn: othersDrawn.map((t) => `${t.trackId}/${t.className}/dt=${t.dtMs ?? "?"}ms/${t.interpolated === true ? "interpolated" : "exact"}`).join(",")
|
|
40540
|
+
}
|
|
40541
|
+
});
|
|
40542
|
+
const others = mode === "scene-boxes" ? othersDrawn.map((t) => ({
|
|
40328
40543
|
...t.bbox,
|
|
40329
40544
|
label: imageLabelText(t.label ?? t.className, t.confidence),
|
|
40330
40545
|
...t.confidence !== void 0 ? { confidence: t.confidence } : {}
|
|
@@ -87637,7 +87852,12 @@ var RetrainCollectionService = class {
|
|
|
87637
87852
|
reason: "unknown-collection"
|
|
87638
87853
|
};
|
|
87639
87854
|
}
|
|
87640
|
-
const
|
|
87855
|
+
const membershipOp = await this.deps.collections.addFrameOp({
|
|
87856
|
+
collectionId: input.collectionId,
|
|
87857
|
+
frameId: recordingFrameId(input.deviceId, input.epochMs),
|
|
87858
|
+
deviceId: input.deviceId
|
|
87859
|
+
});
|
|
87860
|
+
const stored = await this.storeStill(input.deviceId, input.epochMs, membershipOp === null ? [] : [membershipOp]);
|
|
87641
87861
|
if (stored.kind === "refused") {
|
|
87642
87862
|
this.deps.logger.info("retrain collection add refused", {
|
|
87643
87863
|
tags,
|
|
@@ -87649,7 +87869,7 @@ var RetrainCollectionService = class {
|
|
|
87649
87869
|
});
|
|
87650
87870
|
return stored;
|
|
87651
87871
|
}
|
|
87652
|
-
await this.deps.collections.addFrame({
|
|
87872
|
+
if (stored.reused) await this.deps.collections.addFrame({
|
|
87653
87873
|
collectionId: input.collectionId,
|
|
87654
87874
|
frameId: stored.frame.frameId,
|
|
87655
87875
|
deviceId: input.deviceId
|
|
@@ -87714,7 +87934,7 @@ var RetrainCollectionService = class {
|
|
|
87714
87934
|
const ids = await this.deps.collections.listFrameIds(input.collectionId);
|
|
87715
87935
|
return this.deps.retrain.toWireFrames(await this.deps.frames.getMany(ids));
|
|
87716
87936
|
}
|
|
87717
|
-
async storeStill(deviceId, epochMs) {
|
|
87937
|
+
async storeStill(deviceId, epochMs, withRow) {
|
|
87718
87938
|
const existing = await this.deps.frames.get(recordingFrameId(deviceId, epochMs));
|
|
87719
87939
|
if (existing !== null) return {
|
|
87720
87940
|
kind: "stored",
|
|
@@ -87734,7 +87954,7 @@ var RetrainCollectionService = class {
|
|
|
87734
87954
|
deviceId,
|
|
87735
87955
|
epochMs,
|
|
87736
87956
|
bytes: still.jpeg
|
|
87737
|
-
});
|
|
87957
|
+
}, withRow);
|
|
87738
87958
|
return stored.kind === "copied" ? {
|
|
87739
87959
|
kind: "stored",
|
|
87740
87960
|
frame: stored.frame,
|
|
@@ -90057,9 +90277,9 @@ var RetrainService = class {
|
|
|
90057
90277
|
...this.frameUrls(updated.frameId)
|
|
90058
90278
|
};
|
|
90059
90279
|
}
|
|
90060
|
-
/** Un-select:
|
|
90061
|
-
* an annotation whose frame is gone is unopenable
|
|
90062
|
-
*
|
|
90280
|
+
/** Un-select: the frame row, its annotations and its collection memberships
|
|
90281
|
+
* leave in one transaction (an annotation whose frame is gone is unopenable
|
|
90282
|
+
* and unfixable, so they never part); the blob is deleted after the row. */
|
|
90063
90283
|
async deselectFrame(input) {
|
|
90064
90284
|
const frame = await this.deps.frames.get(input.frameId);
|
|
90065
90285
|
if (frame === null) {
|
|
@@ -90078,11 +90298,16 @@ var RetrainService = class {
|
|
|
90078
90298
|
removedAnnotations: await this.removeFrame(frame)
|
|
90079
90299
|
};
|
|
90080
90300
|
}
|
|
90081
|
-
/**
|
|
90301
|
+
/**
|
|
90302
|
+
* Remove a frame from the dataset. The frame row, its annotations and its
|
|
90303
|
+
* collection memberships leave in ONE transaction (a failure removes nothing
|
|
90304
|
+
* and throws); the blob is deleted after the row, never before.
|
|
90305
|
+
*/
|
|
90082
90306
|
async removeFrame(frame) {
|
|
90083
|
-
const
|
|
90084
|
-
await this.deps.memberships?.
|
|
90085
|
-
await this.deps.frames.deleteFrame(frame);
|
|
90307
|
+
const annotationOps = await this.deps.annotations.deleteOpsForFrame(frame.frameId);
|
|
90308
|
+
const membershipOps = await this.deps.memberships?.forgetFrameOps(frame.frameId) ?? [];
|
|
90309
|
+
await this.deps.frames.deleteFrame(frame, [...annotationOps, ...membershipOps]);
|
|
90310
|
+
const removedAnnotations = annotationOps.length;
|
|
90086
90311
|
this.deps.logger.info("retrain frame removed from the dataset", {
|
|
90087
90312
|
tags: { deviceId: frame.deviceId },
|
|
90088
90313
|
meta: {
|
|
@@ -90276,7 +90501,8 @@ var RetrainService = class {
|
|
|
90276
90501
|
* The guarded replace. A caller that names the revision it saw gets exactly
|
|
90277
90502
|
* one attempt: a different current revision is a conflict, never a retry —
|
|
90278
90503
|
* the point is that it must look again. A caller that names none asked for
|
|
90279
|
-
* last-write-wins, so a race lost between our read and the
|
|
90504
|
+
* last-write-wins, so a race lost between our revision read and the records
|
|
90505
|
+
* `transact` that commits the save (a per-frame lock serialises the two) is
|
|
90280
90506
|
* retried on the fresh revision, a bounded number of times.
|
|
90281
90507
|
*/
|
|
90282
90508
|
async replaceGuarded(frame, input, drafts, dismissed) {
|
|
@@ -103903,6 +104129,8 @@ var NC_SYSTEM_EVENT_CATEGORIES = [
|
|
|
103903
104129
|
EventCategory.WorkerOffline,
|
|
103904
104130
|
EventCategory.PipelineNodeInferenceUnavailable,
|
|
103905
104131
|
EventCategory.PipelineDetectionBlind,
|
|
104132
|
+
EventCategory.PipelineMotionNoisy,
|
|
104133
|
+
EventCategory.PipelineMotionCalm,
|
|
103906
104134
|
EventCategory.RecordingHealthStalled,
|
|
103907
104135
|
EventCategory.RecordingHealthOk,
|
|
103908
104136
|
EventCategory.RecordingHealthDegraded,
|
|
@@ -106921,6 +107149,18 @@ Object.freeze({
|
|
|
106921
107149
|
"empty"
|
|
106922
107150
|
]
|
|
106923
107151
|
},
|
|
107152
|
+
{
|
|
107153
|
+
"path": "filterCleaningDue",
|
|
107154
|
+
"valueType": "boolean"
|
|
107155
|
+
},
|
|
107156
|
+
{
|
|
107157
|
+
"path": "machineCareDue",
|
|
107158
|
+
"valueType": "boolean"
|
|
107159
|
+
},
|
|
107160
|
+
{
|
|
107161
|
+
"path": "machineCareRemainingRuns",
|
|
107162
|
+
"valueType": "number"
|
|
107163
|
+
},
|
|
106924
107164
|
{
|
|
106925
107165
|
"path": "halfLoad",
|
|
106926
107166
|
"valueType": "boolean"
|
|
@@ -116215,7 +116455,16 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
116215
116455
|
since: timestamp - toleranceMs,
|
|
116216
116456
|
limit: NC_TRACKS_AT_INSTANT_LIMIT
|
|
116217
116457
|
});
|
|
116218
|
-
return tracksAtInstant([...live, ...persisted.tracks], timestamp, toleranceMs, this.lastFrameDimsByDevice.get(deviceId))
|
|
116458
|
+
return tracksAtInstant([...live, ...persisted.tracks], timestamp, toleranceMs, this.lastFrameDimsByDevice.get(deviceId), (drop) => this.ctx.logger.info("scene-boxes: other track not drawn", {
|
|
116459
|
+
tags: { deviceId },
|
|
116460
|
+
meta: {
|
|
116461
|
+
trackId: drop.trackId,
|
|
116462
|
+
className: drop.className,
|
|
116463
|
+
reason: drop.reason,
|
|
116464
|
+
instant: timestamp,
|
|
116465
|
+
...drop.nearestDtMs !== void 0 ? { nearestDtMs: drop.nearestDtMs } : {}
|
|
116466
|
+
}
|
|
116467
|
+
}));
|
|
116219
116468
|
},
|
|
116220
116469
|
getDeviceName,
|
|
116221
116470
|
getDeviceLocation,
|