@camstack/addon-pipeline-orchestrator 1.1.22 → 1.1.24
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/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-CQmCy3VK.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-CngJtoGC.mjs} +2 -2
- package/dist/{hostInit-ibn4yaBr.mjs → hostInit-DpXCjF-O.mjs} +2 -2
- package/dist/index.js +97 -9
- package/dist/index.mjs +97 -9
- package/dist/remoteEntry.js +1 -1
- package/package.json +1 -1
|
@@ -18,7 +18,7 @@ var e = {
|
|
|
18
18
|
},
|
|
19
19
|
"@camstack/types": {
|
|
20
20
|
name: "@camstack/types",
|
|
21
|
-
version: "1.1.
|
|
21
|
+
version: "1.1.26",
|
|
22
22
|
scope: ["default"],
|
|
23
23
|
loaded: !1,
|
|
24
24
|
from: "addon_pipeline_orchestrator_widgets",
|
|
@@ -33,7 +33,7 @@ var e = {
|
|
|
33
33
|
},
|
|
34
34
|
"@camstack/ui-library": {
|
|
35
35
|
name: "@camstack/ui-library",
|
|
36
|
-
version: "1.1.
|
|
36
|
+
version: "1.1.22",
|
|
37
37
|
scope: ["default"],
|
|
38
38
|
loaded: !1,
|
|
39
39
|
from: "addon_pipeline_orchestrator_widgets",
|
|
@@ -36,7 +36,7 @@ async function r() {
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"@camstack/types": {
|
|
39
|
-
version: "1.1.
|
|
39
|
+
version: "1.1.26",
|
|
40
40
|
scope: "default",
|
|
41
41
|
shareConfig: {
|
|
42
42
|
singleton: !0,
|
|
@@ -99,7 +99,7 @@ async function r() {
|
|
|
99
99
|
}
|
|
100
100
|
},
|
|
101
101
|
"@camstack/ui-library": {
|
|
102
|
-
version: "1.1.
|
|
102
|
+
version: "1.1.22",
|
|
103
103
|
scope: "default",
|
|
104
104
|
shareConfig: {
|
|
105
105
|
singleton: !0,
|
package/dist/index.js
CHANGED
|
@@ -24982,6 +24982,15 @@ var OrchestratorDiagnosticsSchema = object({
|
|
|
24982
24982
|
activeDetectionCount: number().int().min(0)
|
|
24983
24983
|
});
|
|
24984
24984
|
var pipelineOrchestratorActions = defineCustomActions({ dumpState: customAction(_void(), OrchestratorDiagnosticsSchema) });
|
|
24985
|
+
/**
|
|
24986
|
+
* Sentinel returned by `buildDetectionConfig` when the profile-slot READ
|
|
24987
|
+
* itself failed transiently (e.g. `listAllProfileSlots` discovery timeout
|
|
24988
|
+
* while the stream-broker is (re)starting) — as opposed to `null`, which
|
|
24989
|
+
* means "genuinely no assigned slot / not configured". Callers MUST treat
|
|
24990
|
+
* this differently from `null`: never stop active detection on a transient
|
|
24991
|
+
* read failure (the slots almost certainly still exist), and schedule a retry.
|
|
24992
|
+
*/
|
|
24993
|
+
var TRANSIENT_SLOT_READ = Symbol("transient-slot-read");
|
|
24985
24994
|
var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddon {
|
|
24986
24995
|
/** This node's Moleculer nodeId (from this.ctx.kernel.localNodeId). */
|
|
24987
24996
|
localNodeId = "hub";
|
|
@@ -25205,6 +25214,15 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
25205
25214
|
loadShedResumeTimer = null;
|
|
25206
25215
|
/** Per-camera guard so repeated low-fps snapshots don't stack relocate/pause. */
|
|
25207
25216
|
shedInFlight = /* @__PURE__ */ new Set();
|
|
25217
|
+
/**
|
|
25218
|
+
* Per-device backoff for retrying {@link handleDeviceRegistered} after a
|
|
25219
|
+
* TRANSIENT profile-slot read failure ({@link TRANSIENT_SLOT_READ}). Timers
|
|
25220
|
+
* are `unref`'d and cleared on shutdown / device removal.
|
|
25221
|
+
*/
|
|
25222
|
+
slotReadRetryTimers = /* @__PURE__ */ new Map();
|
|
25223
|
+
slotReadRetryAttempts = /* @__PURE__ */ new Map();
|
|
25224
|
+
static SLOT_READ_MAX_RETRIES = 6;
|
|
25225
|
+
static SLOT_READ_RETRY_BASE_MS = 500;
|
|
25208
25226
|
/** Pending `scheduleReconcile` debounce timer. */
|
|
25209
25227
|
reconcileTimer = null;
|
|
25210
25228
|
/** True while `reconcileDispatch` is awaiting the RPC round-trip. */
|
|
@@ -25672,6 +25690,9 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
25672
25690
|
clearTimeout(this.pendingRetryDebounceTimer);
|
|
25673
25691
|
this.pendingRetryDebounceTimer = null;
|
|
25674
25692
|
}
|
|
25693
|
+
for (const t of this.slotReadRetryTimers.values()) clearTimeout(t);
|
|
25694
|
+
this.slotReadRetryTimers.clear();
|
|
25695
|
+
this.slotReadRetryAttempts.clear();
|
|
25675
25696
|
this.unsubDeviceRegistered?.();
|
|
25676
25697
|
this.unsubDeviceRegistered = null;
|
|
25677
25698
|
this.unsubDeviceUnregistered?.();
|
|
@@ -26195,14 +26216,15 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
26195
26216
|
}
|
|
26196
26217
|
/**
|
|
26197
26218
|
* The camera's source-owner node — the ONE node that dials its real RTSP
|
|
26198
|
-
* and hosts its broker.
|
|
26199
|
-
*
|
|
26200
|
-
*
|
|
26201
|
-
*
|
|
26202
|
-
*
|
|
26219
|
+
* and hosts its broker. Driven by the `ingestNode` CLUSTER ROLE
|
|
26220
|
+
* (placement-model §6): a single node ingests every camera and serves the
|
|
26221
|
+
* compressed restream. Defaults to the hub. (Arbitrary ingest ≠ hub is a
|
|
26222
|
+
* later phase; wiring the role here means no call site changes when it lands.
|
|
26223
|
+
* With `remoteSourcingNodes` empty the source-owner stays unmodeled, so this
|
|
26224
|
+
* has no runtime effect until the cross-node transport phase.)
|
|
26203
26225
|
*/
|
|
26204
26226
|
assignSource(_deviceId) {
|
|
26205
|
-
return this.
|
|
26227
|
+
return this.clusterRoles.ingestNode;
|
|
26206
26228
|
}
|
|
26207
26229
|
/**
|
|
26208
26230
|
* The node that owns `deviceId`'s source pull, or `undefined` while
|
|
@@ -26469,6 +26491,13 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
26469
26491
|
this.recordAudioAssignment(deviceId, this.localNodeId, false);
|
|
26470
26492
|
return this.localNodeId;
|
|
26471
26493
|
}
|
|
26494
|
+
if (!preferredNode) {
|
|
26495
|
+
const roleNode = this.clusterRoles.audioNode;
|
|
26496
|
+
if (loads.some((load) => load.nodeId === roleNode)) {
|
|
26497
|
+
this.recordAudioAssignment(deviceId, roleNode, false);
|
|
26498
|
+
return roleNode;
|
|
26499
|
+
}
|
|
26500
|
+
}
|
|
26472
26501
|
const decision = balanceAudio({
|
|
26473
26502
|
nodes: loads,
|
|
26474
26503
|
preferredNode
|
|
@@ -26961,6 +26990,47 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
26961
26990
|
}
|
|
26962
26991
|
}
|
|
26963
26992
|
/**
|
|
26993
|
+
* Bounded per-device retry of {@link handleDeviceRegistered} after a
|
|
26994
|
+
* TRANSIENT profile-slot read failure ({@link TRANSIENT_SLOT_READ}). The
|
|
26995
|
+
* read (`listAllProfileSlots`) can time out during a broker (re)start; we
|
|
26996
|
+
* must neither stop a running camera nor leave a not-yet-started camera
|
|
26997
|
+
* stranded — so re-run the registration handler with exponential backoff
|
|
26998
|
+
* until the read succeeds or the budget is exhausted. Reset on any
|
|
26999
|
+
* successful read ({@link clearSlotReadRetry}).
|
|
27000
|
+
*/
|
|
27001
|
+
scheduleSlotReadRetry(deviceId) {
|
|
27002
|
+
const attempts = this.slotReadRetryAttempts.get(deviceId) ?? 0;
|
|
27003
|
+
if (attempts >= PipelineOrchestratorAddon.SLOT_READ_MAX_RETRIES) {
|
|
27004
|
+
this.ctx.logger.warn("slot-read retry budget exhausted — leaving detection as-is", { tags: { deviceId } });
|
|
27005
|
+
this.slotReadRetryAttempts.delete(deviceId);
|
|
27006
|
+
return;
|
|
27007
|
+
}
|
|
27008
|
+
const existing = this.slotReadRetryTimers.get(deviceId);
|
|
27009
|
+
if (existing) clearTimeout(existing);
|
|
27010
|
+
const delay = PipelineOrchestratorAddon.SLOT_READ_RETRY_BASE_MS * 2 ** attempts;
|
|
27011
|
+
this.slotReadRetryAttempts.set(deviceId, attempts + 1);
|
|
27012
|
+
const timer = setTimeout(() => {
|
|
27013
|
+
this.slotReadRetryTimers.delete(deviceId);
|
|
27014
|
+
this.handleDeviceRegistered(deviceId).catch((err) => {
|
|
27015
|
+
this.ctx.logger.debug("slot-read retry: handleDeviceRegistered failed", {
|
|
27016
|
+
tags: { deviceId },
|
|
27017
|
+
meta: { error: errMsg(err) }
|
|
27018
|
+
});
|
|
27019
|
+
});
|
|
27020
|
+
}, delay);
|
|
27021
|
+
if (typeof timer === "object" && timer !== null && "unref" in timer) timer.unref();
|
|
27022
|
+
this.slotReadRetryTimers.set(deviceId, timer);
|
|
27023
|
+
}
|
|
27024
|
+
/** Cancel any pending slot-read retry + reset the budget for a device. */
|
|
27025
|
+
clearSlotReadRetry(deviceId) {
|
|
27026
|
+
const existing = this.slotReadRetryTimers.get(deviceId);
|
|
27027
|
+
if (existing) {
|
|
27028
|
+
clearTimeout(existing);
|
|
27029
|
+
this.slotReadRetryTimers.delete(deviceId);
|
|
27030
|
+
}
|
|
27031
|
+
this.slotReadRetryAttempts.delete(deviceId);
|
|
27032
|
+
}
|
|
27033
|
+
/**
|
|
26964
27034
|
* Coalesce bursts of capacity/eligibility/readiness signals into a single
|
|
26965
27035
|
* `retryPendingDispatches` pass. Mirrors `scheduleReconcile`, but on a
|
|
26966
27036
|
* dedicated (longer) debounce so a raise-cap / node-connect flurry doesn't
|
|
@@ -28837,7 +28907,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
28837
28907
|
*/
|
|
28838
28908
|
async fetchAssignedProfiles(deviceId) {
|
|
28839
28909
|
const api = this.api;
|
|
28840
|
-
if (!api) return
|
|
28910
|
+
if (!api) return null;
|
|
28841
28911
|
try {
|
|
28842
28912
|
const slots = await api.streamBroker.listAllProfileSlots.query();
|
|
28843
28913
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -28847,8 +28917,12 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
28847
28917
|
out.set(slot.profile, slot.sourceCamStreamId);
|
|
28848
28918
|
}
|
|
28849
28919
|
return out;
|
|
28850
|
-
} catch {
|
|
28851
|
-
|
|
28920
|
+
} catch (err) {
|
|
28921
|
+
this.ctx.logger.debug("fetchAssignedProfiles: slot read failed (transient) — treating as unknown", {
|
|
28922
|
+
tags: { deviceId },
|
|
28923
|
+
meta: { error: errMsg(err) }
|
|
28924
|
+
});
|
|
28925
|
+
return null;
|
|
28852
28926
|
}
|
|
28853
28927
|
}
|
|
28854
28928
|
/**
|
|
@@ -28867,6 +28941,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
28867
28941
|
*/
|
|
28868
28942
|
async buildDetectionConfig(deviceId) {
|
|
28869
28943
|
const assigned = await this.fetchAssignedProfiles(deviceId);
|
|
28944
|
+
if (assigned === null) return TRANSIENT_SLOT_READ;
|
|
28870
28945
|
if (assigned.size === 0) return null;
|
|
28871
28946
|
const resolved = await this.resolveDeviceDetectionSettings(deviceId);
|
|
28872
28947
|
if (!resolved) return null;
|
|
@@ -29010,6 +29085,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
29010
29085
|
}
|
|
29011
29086
|
/** Stop detection and purge all persisted config for a removed device. */
|
|
29012
29087
|
async handleDeviceUnregistered(deviceId) {
|
|
29088
|
+
this.clearSlotReadRetry(deviceId);
|
|
29013
29089
|
await this.stopDetection(deviceId);
|
|
29014
29090
|
this.zonesProvider?.forgetDevice(deviceId);
|
|
29015
29091
|
this.zoneRulesProvider?.forgetDevice(deviceId);
|
|
@@ -29031,6 +29107,12 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
29031
29107
|
const log = this.ctx.logger.withTags({ deviceId });
|
|
29032
29108
|
log.info("handleDeviceRegistered", { meta: { phase: "handleDeviceRegistered" } });
|
|
29033
29109
|
const config = await this.buildDetectionConfig(deviceId);
|
|
29110
|
+
if (config === TRANSIENT_SLOT_READ) {
|
|
29111
|
+
log.warn("Profile-slot read failed transiently — keeping detection, scheduling retry");
|
|
29112
|
+
this.scheduleSlotReadRetry(deviceId);
|
|
29113
|
+
return;
|
|
29114
|
+
}
|
|
29115
|
+
this.clearSlotReadRetry(deviceId);
|
|
29034
29116
|
log.info("[pipeline-orchestrator] buildDetectionConfig", config ? { meta: {
|
|
29035
29117
|
enabled: config.enabled,
|
|
29036
29118
|
motionStreamId: config.motionStreamId,
|
|
@@ -29096,6 +29178,12 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
29096
29178
|
const log = this.ctx.logger.withTags({ deviceId });
|
|
29097
29179
|
if (this.activeDetections.has(deviceId)) await this.stopDetection(deviceId);
|
|
29098
29180
|
const config = await this.buildDetectionConfig(deviceId);
|
|
29181
|
+
if (config === TRANSIENT_SLOT_READ) {
|
|
29182
|
+
log.warn("Settings changed — slot read failed transiently, scheduling retry");
|
|
29183
|
+
this.scheduleSlotReadRetry(deviceId);
|
|
29184
|
+
return;
|
|
29185
|
+
}
|
|
29186
|
+
this.clearSlotReadRetry(deviceId);
|
|
29099
29187
|
if (!config) {
|
|
29100
29188
|
log.info("Settings changed — no assigned slot, detection stopped");
|
|
29101
29189
|
return;
|
package/dist/index.mjs
CHANGED
|
@@ -24978,6 +24978,15 @@ var OrchestratorDiagnosticsSchema = object({
|
|
|
24978
24978
|
activeDetectionCount: number().int().min(0)
|
|
24979
24979
|
});
|
|
24980
24980
|
var pipelineOrchestratorActions = defineCustomActions({ dumpState: customAction(_void(), OrchestratorDiagnosticsSchema) });
|
|
24981
|
+
/**
|
|
24982
|
+
* Sentinel returned by `buildDetectionConfig` when the profile-slot READ
|
|
24983
|
+
* itself failed transiently (e.g. `listAllProfileSlots` discovery timeout
|
|
24984
|
+
* while the stream-broker is (re)starting) — as opposed to `null`, which
|
|
24985
|
+
* means "genuinely no assigned slot / not configured". Callers MUST treat
|
|
24986
|
+
* this differently from `null`: never stop active detection on a transient
|
|
24987
|
+
* read failure (the slots almost certainly still exist), and schedule a retry.
|
|
24988
|
+
*/
|
|
24989
|
+
var TRANSIENT_SLOT_READ = Symbol("transient-slot-read");
|
|
24981
24990
|
var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddon {
|
|
24982
24991
|
/** This node's Moleculer nodeId (from this.ctx.kernel.localNodeId). */
|
|
24983
24992
|
localNodeId = "hub";
|
|
@@ -25201,6 +25210,15 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
25201
25210
|
loadShedResumeTimer = null;
|
|
25202
25211
|
/** Per-camera guard so repeated low-fps snapshots don't stack relocate/pause. */
|
|
25203
25212
|
shedInFlight = /* @__PURE__ */ new Set();
|
|
25213
|
+
/**
|
|
25214
|
+
* Per-device backoff for retrying {@link handleDeviceRegistered} after a
|
|
25215
|
+
* TRANSIENT profile-slot read failure ({@link TRANSIENT_SLOT_READ}). Timers
|
|
25216
|
+
* are `unref`'d and cleared on shutdown / device removal.
|
|
25217
|
+
*/
|
|
25218
|
+
slotReadRetryTimers = /* @__PURE__ */ new Map();
|
|
25219
|
+
slotReadRetryAttempts = /* @__PURE__ */ new Map();
|
|
25220
|
+
static SLOT_READ_MAX_RETRIES = 6;
|
|
25221
|
+
static SLOT_READ_RETRY_BASE_MS = 500;
|
|
25204
25222
|
/** Pending `scheduleReconcile` debounce timer. */
|
|
25205
25223
|
reconcileTimer = null;
|
|
25206
25224
|
/** True while `reconcileDispatch` is awaiting the RPC round-trip. */
|
|
@@ -25668,6 +25686,9 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
25668
25686
|
clearTimeout(this.pendingRetryDebounceTimer);
|
|
25669
25687
|
this.pendingRetryDebounceTimer = null;
|
|
25670
25688
|
}
|
|
25689
|
+
for (const t of this.slotReadRetryTimers.values()) clearTimeout(t);
|
|
25690
|
+
this.slotReadRetryTimers.clear();
|
|
25691
|
+
this.slotReadRetryAttempts.clear();
|
|
25671
25692
|
this.unsubDeviceRegistered?.();
|
|
25672
25693
|
this.unsubDeviceRegistered = null;
|
|
25673
25694
|
this.unsubDeviceUnregistered?.();
|
|
@@ -26191,14 +26212,15 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
26191
26212
|
}
|
|
26192
26213
|
/**
|
|
26193
26214
|
* The camera's source-owner node — the ONE node that dials its real RTSP
|
|
26194
|
-
* and hosts its broker.
|
|
26195
|
-
*
|
|
26196
|
-
*
|
|
26197
|
-
*
|
|
26198
|
-
*
|
|
26215
|
+
* and hosts its broker. Driven by the `ingestNode` CLUSTER ROLE
|
|
26216
|
+
* (placement-model §6): a single node ingests every camera and serves the
|
|
26217
|
+
* compressed restream. Defaults to the hub. (Arbitrary ingest ≠ hub is a
|
|
26218
|
+
* later phase; wiring the role here means no call site changes when it lands.
|
|
26219
|
+
* With `remoteSourcingNodes` empty the source-owner stays unmodeled, so this
|
|
26220
|
+
* has no runtime effect until the cross-node transport phase.)
|
|
26199
26221
|
*/
|
|
26200
26222
|
assignSource(_deviceId) {
|
|
26201
|
-
return this.
|
|
26223
|
+
return this.clusterRoles.ingestNode;
|
|
26202
26224
|
}
|
|
26203
26225
|
/**
|
|
26204
26226
|
* The node that owns `deviceId`'s source pull, or `undefined` while
|
|
@@ -26465,6 +26487,13 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
26465
26487
|
this.recordAudioAssignment(deviceId, this.localNodeId, false);
|
|
26466
26488
|
return this.localNodeId;
|
|
26467
26489
|
}
|
|
26490
|
+
if (!preferredNode) {
|
|
26491
|
+
const roleNode = this.clusterRoles.audioNode;
|
|
26492
|
+
if (loads.some((load) => load.nodeId === roleNode)) {
|
|
26493
|
+
this.recordAudioAssignment(deviceId, roleNode, false);
|
|
26494
|
+
return roleNode;
|
|
26495
|
+
}
|
|
26496
|
+
}
|
|
26468
26497
|
const decision = balanceAudio({
|
|
26469
26498
|
nodes: loads,
|
|
26470
26499
|
preferredNode
|
|
@@ -26957,6 +26986,47 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
26957
26986
|
}
|
|
26958
26987
|
}
|
|
26959
26988
|
/**
|
|
26989
|
+
* Bounded per-device retry of {@link handleDeviceRegistered} after a
|
|
26990
|
+
* TRANSIENT profile-slot read failure ({@link TRANSIENT_SLOT_READ}). The
|
|
26991
|
+
* read (`listAllProfileSlots`) can time out during a broker (re)start; we
|
|
26992
|
+
* must neither stop a running camera nor leave a not-yet-started camera
|
|
26993
|
+
* stranded — so re-run the registration handler with exponential backoff
|
|
26994
|
+
* until the read succeeds or the budget is exhausted. Reset on any
|
|
26995
|
+
* successful read ({@link clearSlotReadRetry}).
|
|
26996
|
+
*/
|
|
26997
|
+
scheduleSlotReadRetry(deviceId) {
|
|
26998
|
+
const attempts = this.slotReadRetryAttempts.get(deviceId) ?? 0;
|
|
26999
|
+
if (attempts >= PipelineOrchestratorAddon.SLOT_READ_MAX_RETRIES) {
|
|
27000
|
+
this.ctx.logger.warn("slot-read retry budget exhausted — leaving detection as-is", { tags: { deviceId } });
|
|
27001
|
+
this.slotReadRetryAttempts.delete(deviceId);
|
|
27002
|
+
return;
|
|
27003
|
+
}
|
|
27004
|
+
const existing = this.slotReadRetryTimers.get(deviceId);
|
|
27005
|
+
if (existing) clearTimeout(existing);
|
|
27006
|
+
const delay = PipelineOrchestratorAddon.SLOT_READ_RETRY_BASE_MS * 2 ** attempts;
|
|
27007
|
+
this.slotReadRetryAttempts.set(deviceId, attempts + 1);
|
|
27008
|
+
const timer = setTimeout(() => {
|
|
27009
|
+
this.slotReadRetryTimers.delete(deviceId);
|
|
27010
|
+
this.handleDeviceRegistered(deviceId).catch((err) => {
|
|
27011
|
+
this.ctx.logger.debug("slot-read retry: handleDeviceRegistered failed", {
|
|
27012
|
+
tags: { deviceId },
|
|
27013
|
+
meta: { error: errMsg(err) }
|
|
27014
|
+
});
|
|
27015
|
+
});
|
|
27016
|
+
}, delay);
|
|
27017
|
+
if (typeof timer === "object" && timer !== null && "unref" in timer) timer.unref();
|
|
27018
|
+
this.slotReadRetryTimers.set(deviceId, timer);
|
|
27019
|
+
}
|
|
27020
|
+
/** Cancel any pending slot-read retry + reset the budget for a device. */
|
|
27021
|
+
clearSlotReadRetry(deviceId) {
|
|
27022
|
+
const existing = this.slotReadRetryTimers.get(deviceId);
|
|
27023
|
+
if (existing) {
|
|
27024
|
+
clearTimeout(existing);
|
|
27025
|
+
this.slotReadRetryTimers.delete(deviceId);
|
|
27026
|
+
}
|
|
27027
|
+
this.slotReadRetryAttempts.delete(deviceId);
|
|
27028
|
+
}
|
|
27029
|
+
/**
|
|
26960
27030
|
* Coalesce bursts of capacity/eligibility/readiness signals into a single
|
|
26961
27031
|
* `retryPendingDispatches` pass. Mirrors `scheduleReconcile`, but on a
|
|
26962
27032
|
* dedicated (longer) debounce so a raise-cap / node-connect flurry doesn't
|
|
@@ -28833,7 +28903,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
28833
28903
|
*/
|
|
28834
28904
|
async fetchAssignedProfiles(deviceId) {
|
|
28835
28905
|
const api = this.api;
|
|
28836
|
-
if (!api) return
|
|
28906
|
+
if (!api) return null;
|
|
28837
28907
|
try {
|
|
28838
28908
|
const slots = await api.streamBroker.listAllProfileSlots.query();
|
|
28839
28909
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -28843,8 +28913,12 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
28843
28913
|
out.set(slot.profile, slot.sourceCamStreamId);
|
|
28844
28914
|
}
|
|
28845
28915
|
return out;
|
|
28846
|
-
} catch {
|
|
28847
|
-
|
|
28916
|
+
} catch (err) {
|
|
28917
|
+
this.ctx.logger.debug("fetchAssignedProfiles: slot read failed (transient) — treating as unknown", {
|
|
28918
|
+
tags: { deviceId },
|
|
28919
|
+
meta: { error: errMsg(err) }
|
|
28920
|
+
});
|
|
28921
|
+
return null;
|
|
28848
28922
|
}
|
|
28849
28923
|
}
|
|
28850
28924
|
/**
|
|
@@ -28863,6 +28937,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
28863
28937
|
*/
|
|
28864
28938
|
async buildDetectionConfig(deviceId) {
|
|
28865
28939
|
const assigned = await this.fetchAssignedProfiles(deviceId);
|
|
28940
|
+
if (assigned === null) return TRANSIENT_SLOT_READ;
|
|
28866
28941
|
if (assigned.size === 0) return null;
|
|
28867
28942
|
const resolved = await this.resolveDeviceDetectionSettings(deviceId);
|
|
28868
28943
|
if (!resolved) return null;
|
|
@@ -29006,6 +29081,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
29006
29081
|
}
|
|
29007
29082
|
/** Stop detection and purge all persisted config for a removed device. */
|
|
29008
29083
|
async handleDeviceUnregistered(deviceId) {
|
|
29084
|
+
this.clearSlotReadRetry(deviceId);
|
|
29009
29085
|
await this.stopDetection(deviceId);
|
|
29010
29086
|
this.zonesProvider?.forgetDevice(deviceId);
|
|
29011
29087
|
this.zoneRulesProvider?.forgetDevice(deviceId);
|
|
@@ -29027,6 +29103,12 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
29027
29103
|
const log = this.ctx.logger.withTags({ deviceId });
|
|
29028
29104
|
log.info("handleDeviceRegistered", { meta: { phase: "handleDeviceRegistered" } });
|
|
29029
29105
|
const config = await this.buildDetectionConfig(deviceId);
|
|
29106
|
+
if (config === TRANSIENT_SLOT_READ) {
|
|
29107
|
+
log.warn("Profile-slot read failed transiently — keeping detection, scheduling retry");
|
|
29108
|
+
this.scheduleSlotReadRetry(deviceId);
|
|
29109
|
+
return;
|
|
29110
|
+
}
|
|
29111
|
+
this.clearSlotReadRetry(deviceId);
|
|
29030
29112
|
log.info("[pipeline-orchestrator] buildDetectionConfig", config ? { meta: {
|
|
29031
29113
|
enabled: config.enabled,
|
|
29032
29114
|
motionStreamId: config.motionStreamId,
|
|
@@ -29092,6 +29174,12 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
29092
29174
|
const log = this.ctx.logger.withTags({ deviceId });
|
|
29093
29175
|
if (this.activeDetections.has(deviceId)) await this.stopDetection(deviceId);
|
|
29094
29176
|
const config = await this.buildDetectionConfig(deviceId);
|
|
29177
|
+
if (config === TRANSIENT_SLOT_READ) {
|
|
29178
|
+
log.warn("Settings changed — slot read failed transiently, scheduling retry");
|
|
29179
|
+
this.scheduleSlotReadRetry(deviceId);
|
|
29180
|
+
return;
|
|
29181
|
+
}
|
|
29182
|
+
this.clearSlotReadRetry(deviceId);
|
|
29095
29183
|
if (!config) {
|
|
29096
29184
|
log.info("Settings changed — no assigned slot, detection stopped");
|
|
29097
29185
|
return;
|
package/dist/remoteEntry.js
CHANGED
|
@@ -30,7 +30,7 @@ async function d(e) {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
async function f() {
|
|
33
|
-
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-
|
|
33
|
+
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-CngJtoGC.mjs")).catch((e) => {
|
|
34
34
|
throw l = void 0, e;
|
|
35
35
|
}), l;
|
|
36
36
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-pipeline-orchestrator",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.24",
|
|
4
4
|
"description": "Hub-side camera-to-agent load balancer — tracks runner capacity and dispatches attachCamera calls to the optimal pipeline-runner instance",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|