@ziztechnology/dial-library 0.0.11 → 0.0.13
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/README.md +16 -4
- package/dist/index.d.mts +67 -2
- package/dist/index.mjs +453 -231
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -43,6 +43,45 @@ const dialSdkDebugWarn = (moduleName, scope, phase, data) => {
|
|
|
43
43
|
} catch {}
|
|
44
44
|
};
|
|
45
45
|
//#endregion
|
|
46
|
+
//#region src/helper/sensor.ts
|
|
47
|
+
const extractFreshVector = (metric, capturedAtMs, maxSampleAgeMs) => {
|
|
48
|
+
if (!metric?.available || !Number.isFinite(capturedAtMs) || !Number.isFinite(metric.sampledAtMs)) return null;
|
|
49
|
+
const ageMs = capturedAtMs - metric.sampledAtMs;
|
|
50
|
+
if (ageMs < 0 || ageMs > maxSampleAgeMs) return null;
|
|
51
|
+
const { x, y, z } = metric.value;
|
|
52
|
+
return [
|
|
53
|
+
x,
|
|
54
|
+
y,
|
|
55
|
+
z
|
|
56
|
+
].every(Number.isFinite) ? {
|
|
57
|
+
sampledAtMs: metric.sampledAtMs,
|
|
58
|
+
value: metric.value
|
|
59
|
+
} : null;
|
|
60
|
+
};
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/helper/validation.ts
|
|
63
|
+
const requirePositiveFinite = (name, value, debug) => {
|
|
64
|
+
dialSdkDebugWarn(debug.moduleName, debug.scope, "called", {
|
|
65
|
+
name,
|
|
66
|
+
value
|
|
67
|
+
});
|
|
68
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
69
|
+
const error = /* @__PURE__ */ new RangeError(`${name} must be a positive finite number`);
|
|
70
|
+
dialSdkDebugWarn(debug.moduleName, debug.scope, "threw", error);
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
dialSdkDebugWarn(debug.moduleName, debug.scope, "returned", { result: value });
|
|
74
|
+
return value;
|
|
75
|
+
};
|
|
76
|
+
const isRecord = (value, debug) => {
|
|
77
|
+
const result = typeof value === "object" && value !== null && !Array.isArray(value);
|
|
78
|
+
if (debug) dialSdkDebugWarn(debug.moduleName, debug.scope, "returned", {
|
|
79
|
+
value,
|
|
80
|
+
result
|
|
81
|
+
});
|
|
82
|
+
return result;
|
|
83
|
+
};
|
|
84
|
+
//#endregion
|
|
46
85
|
//#region src/sensor/check_status.ts
|
|
47
86
|
const DEBUG_MODULE$5 = "check_status.ts";
|
|
48
87
|
const DEVICE_RIGHT = {
|
|
@@ -56,7 +95,7 @@ const ZERO_VECTOR$1 = {
|
|
|
56
95
|
z: 0
|
|
57
96
|
};
|
|
58
97
|
const DEFAULT_DRIVING_STATUS_TUNING = Object.freeze({
|
|
59
|
-
version: "driving-status-calibrated-2026-07",
|
|
98
|
+
version: "driving-status-calibrated-2026-07-17",
|
|
60
99
|
maxSampleAgeMs: 1e3,
|
|
61
100
|
gravityFilterTimeConstantMs: 1e3,
|
|
62
101
|
biasFilterTimeConstantMs: 1e4,
|
|
@@ -69,7 +108,7 @@ const DEFAULT_DRIVING_STATUS_TUNING = Object.freeze({
|
|
|
69
108
|
stoppedRawGyroThreshold: .065,
|
|
70
109
|
drivingGyroThreshold: .04,
|
|
71
110
|
drivingRawGyroThreshold: .08,
|
|
72
|
-
stoppedConfirmationMs:
|
|
111
|
+
stoppedConfirmationMs: 3e3,
|
|
73
112
|
drivingConfirmationMs: 600,
|
|
74
113
|
longitudinalWindowMs: 800,
|
|
75
114
|
longitudinalMinimumSamples: 5,
|
|
@@ -86,11 +125,15 @@ const DEFAULT_DRIVING_STATUS_TUNING = Object.freeze({
|
|
|
86
125
|
verticalBumpRatio: 1.25,
|
|
87
126
|
longitudinalReversalWindowMs: 400,
|
|
88
127
|
turnWindowMs: 1500,
|
|
89
|
-
turnMinimumSamples:
|
|
90
|
-
turnYawThreshold: .
|
|
91
|
-
turnConfirmationMs:
|
|
128
|
+
turnMinimumSamples: 3,
|
|
129
|
+
turnYawThreshold: .08,
|
|
130
|
+
turnConfirmationMs: 400,
|
|
92
131
|
turnIntegratedAngleThreshold: .12,
|
|
93
132
|
turnSignCoherence: .7,
|
|
133
|
+
turnYawDominanceThreshold: .6,
|
|
134
|
+
turnLateralEvidenceThreshold: .25,
|
|
135
|
+
turnVerticalNoiseFloor: .1,
|
|
136
|
+
turnVerticalBumpRatio: 1.25,
|
|
94
137
|
turnExitYawThreshold: .04,
|
|
95
138
|
turnExitConfirmationMs: 700,
|
|
96
139
|
turnClosureWindowMs: 1500
|
|
@@ -154,10 +197,16 @@ const resolveDrivingStatusTuning = (overrides = {}) => {
|
|
|
154
197
|
"turnYawThreshold",
|
|
155
198
|
"turnConfirmationMs",
|
|
156
199
|
"turnIntegratedAngleThreshold",
|
|
200
|
+
"turnLateralEvidenceThreshold",
|
|
201
|
+
"turnVerticalNoiseFloor",
|
|
202
|
+
"turnVerticalBumpRatio",
|
|
157
203
|
"turnExitYawThreshold",
|
|
158
204
|
"turnExitConfirmationMs",
|
|
159
205
|
"turnClosureWindowMs"
|
|
160
|
-
])
|
|
206
|
+
]) requirePositiveFinite(field, tuning[field], {
|
|
207
|
+
moduleName: DEBUG_MODULE$5,
|
|
208
|
+
scope: "assertPositiveFinite"
|
|
209
|
+
});
|
|
161
210
|
for (const field of [
|
|
162
211
|
"motionMinimumSamples",
|
|
163
212
|
"longitudinalMinimumSamples",
|
|
@@ -167,7 +216,8 @@ const resolveDrivingStatusTuning = (overrides = {}) => {
|
|
|
167
216
|
for (const field of [
|
|
168
217
|
"longitudinalSignCoherence",
|
|
169
218
|
"rapidLongitudinalSignCoherence",
|
|
170
|
-
"turnSignCoherence"
|
|
219
|
+
"turnSignCoherence",
|
|
220
|
+
"turnYawDominanceThreshold"
|
|
171
221
|
]) {
|
|
172
222
|
const value = tuning[field];
|
|
173
223
|
if (!Number.isFinite(value) || value <= .5 || value > 1) throwLoggedRangeError("resolveDrivingStatusTuning", `${field} must be greater than 0.5 and less than or equal to 1`);
|
|
@@ -192,22 +242,25 @@ const extractDrivingSnapshotMetrics = (info, tuning = DEFAULT_DRIVING_STATUS_TUN
|
|
|
192
242
|
dialSdkDebugWarn(DEBUG_MODULE$5, "extractDrivingSnapshotMetrics", "returned invalid capture time", { info });
|
|
193
243
|
return null;
|
|
194
244
|
}
|
|
195
|
-
const linearAcceleration =
|
|
196
|
-
const gyroscope =
|
|
197
|
-
const gravityMetric =
|
|
198
|
-
|
|
245
|
+
const linearAcceleration = extractFreshVector(info.linearAcceleration, info.capturedAtMs, tuning.maxSampleAgeMs);
|
|
246
|
+
const gyroscope = extractFreshVector(info.gyroscope, info.capturedAtMs, tuning.maxSampleAgeMs);
|
|
247
|
+
const gravityMetric = extractFreshVector(info.gravity, info.capturedAtMs, tuning.maxSampleAgeMs);
|
|
248
|
+
const gravity = availableCorrectedGravity(correction, info.capturedAtMs, tuning.maxSampleAgeMs) ?? (correction.filteredGravity !== void 0 && gravityMetric !== null ? {
|
|
249
|
+
value: correction.filteredGravity,
|
|
250
|
+
sampledAtMs: gravityMetric.sampledAtMs
|
|
251
|
+
} : gravityMetric);
|
|
252
|
+
if (linearAcceleration === null || gyroscope === null || gravity === null) {
|
|
199
253
|
dialSdkDebugWarn(DEBUG_MODULE$5, "extractDrivingSnapshotMetrics", "returned unavailable vectors", {
|
|
200
254
|
linearAcceleration,
|
|
201
255
|
gyroscope,
|
|
202
|
-
gravity
|
|
256
|
+
gravity
|
|
203
257
|
});
|
|
204
258
|
return null;
|
|
205
259
|
}
|
|
206
|
-
const
|
|
207
|
-
const frame = buildVehicleFrame(gravity, tuning.vehicleRightMinimumNorm);
|
|
260
|
+
const frame = buildVehicleFrame(gravity.value, tuning.vehicleRightMinimumNorm);
|
|
208
261
|
if (frame === null) {
|
|
209
262
|
dialSdkDebugWarn(DEBUG_MODULE$5, "extractDrivingSnapshotMetrics", "returned invalid vehicle frame", {
|
|
210
|
-
gravity,
|
|
263
|
+
gravity: gravity.value,
|
|
211
264
|
vehicleRightMinimumNorm: tuning.vehicleRightMinimumNorm
|
|
212
265
|
});
|
|
213
266
|
return null;
|
|
@@ -218,7 +271,7 @@ const extractDrivingSnapshotMetrics = (info, tuning = DEFAULT_DRIVING_STATUS_TUN
|
|
|
218
271
|
capturedAtMs: info.capturedAtMs,
|
|
219
272
|
linearAccelerationSampledAtMs: linearAcceleration.sampledAtMs,
|
|
220
273
|
gyroscopeSampledAtMs: gyroscope.sampledAtMs,
|
|
221
|
-
gravitySampledAtMs:
|
|
274
|
+
gravitySampledAtMs: gravity.sampledAtMs,
|
|
222
275
|
longitudinal: dot(correctedLinear, frame.forward),
|
|
223
276
|
lateral: dot(correctedLinear, frame.right),
|
|
224
277
|
vertical: dot(correctedLinear, frame.up),
|
|
@@ -287,16 +340,17 @@ const classifyInstantaneousEvidence = (metrics, tuning) => {
|
|
|
287
340
|
rejectionReason: null
|
|
288
341
|
};
|
|
289
342
|
};
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
343
|
+
const availableCorrectedGravity = (correction, capturedAtMs, maxSampleAgeMs) => {
|
|
344
|
+
const { filteredGravity, filteredGravitySampledAtMs } = correction;
|
|
345
|
+
if (filteredGravity === void 0 || filteredGravitySampledAtMs === void 0 || !isFresh(filteredGravitySampledAtMs, capturedAtMs, maxSampleAgeMs)) return null;
|
|
346
|
+
const { x, y, z } = filteredGravity;
|
|
293
347
|
return [
|
|
294
348
|
x,
|
|
295
349
|
y,
|
|
296
350
|
z
|
|
297
351
|
].every(Number.isFinite) ? {
|
|
298
|
-
value:
|
|
299
|
-
sampledAtMs:
|
|
352
|
+
value: filteredGravity,
|
|
353
|
+
sampledAtMs: filteredGravitySampledAtMs
|
|
300
354
|
} : null;
|
|
301
355
|
};
|
|
302
356
|
const isFresh = (sampledAtMs, capturedAtMs, maxSampleAgeMs) => {
|
|
@@ -325,13 +379,6 @@ const normalize = (value) => {
|
|
|
325
379
|
const length = magnitude(value);
|
|
326
380
|
return Number.isFinite(length) && length > Number.EPSILON ? scale(value, 1 / length) : null;
|
|
327
381
|
};
|
|
328
|
-
const assertPositiveFinite = (name, value) => {
|
|
329
|
-
if (!Number.isFinite(value) || value <= 0) {
|
|
330
|
-
const error = /* @__PURE__ */ new RangeError(`${name} must be a positive finite number`);
|
|
331
|
-
dialSdkDebugWarn(DEBUG_MODULE$5, "assertPositiveFinite", "threw", error);
|
|
332
|
-
throw error;
|
|
333
|
-
}
|
|
334
|
-
};
|
|
335
382
|
const throwLoggedRangeError = (scope, message) => {
|
|
336
383
|
const error = new RangeError(message);
|
|
337
384
|
dialSdkDebugWarn(DEBUG_MODULE$5, scope, "threw", error);
|
|
@@ -397,8 +444,51 @@ const CAR_RUNNING_STATUS_PRIORITY = {
|
|
|
397
444
|
SUDDEN_BRAKING: 5
|
|
398
445
|
};
|
|
399
446
|
//#endregion
|
|
447
|
+
//#region src/helper/async.ts
|
|
448
|
+
const createAbortError = (message, debug) => {
|
|
449
|
+
dialSdkDebugWarn(debug.moduleName, debug.scope, "called");
|
|
450
|
+
if (typeof DOMException === "function") {
|
|
451
|
+
const result = new DOMException(message, "AbortError");
|
|
452
|
+
dialSdkDebugWarn(debug.moduleName, debug.scope, "returned DOMException", { result });
|
|
453
|
+
return result;
|
|
454
|
+
}
|
|
455
|
+
const error = new Error(message);
|
|
456
|
+
error.name = "AbortError";
|
|
457
|
+
dialSdkDebugWarn(debug.moduleName, debug.scope, "returned Error", { result: error });
|
|
458
|
+
return error;
|
|
459
|
+
};
|
|
460
|
+
const isAbortError = (error, debug) => {
|
|
461
|
+
const result = error instanceof Error && error.name === "AbortError";
|
|
462
|
+
if (debug) dialSdkDebugWarn(debug.moduleName, debug.scope, "returned", {
|
|
463
|
+
error,
|
|
464
|
+
result
|
|
465
|
+
});
|
|
466
|
+
return result;
|
|
467
|
+
};
|
|
468
|
+
const invokeSafely = (callback, args, onError) => {
|
|
469
|
+
try {
|
|
470
|
+
callback(...args);
|
|
471
|
+
return true;
|
|
472
|
+
} catch (error) {
|
|
473
|
+
onError(error);
|
|
474
|
+
return false;
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
//#endregion
|
|
400
478
|
//#region src/sensor/driving_expressions.ts
|
|
401
479
|
const DEBUG_MODULE$3 = "driving_expressions.ts";
|
|
480
|
+
const IS_RECORD_DEBUG$1 = {
|
|
481
|
+
moduleName: DEBUG_MODULE$3,
|
|
482
|
+
scope: "isRecord"
|
|
483
|
+
};
|
|
484
|
+
const REQUIRE_POSITIVE_FINITE_DEBUG$1 = {
|
|
485
|
+
moduleName: DEBUG_MODULE$3,
|
|
486
|
+
scope: "requirePositiveFinite"
|
|
487
|
+
};
|
|
488
|
+
const CREATE_ABORT_ERROR_DEBUG$1 = {
|
|
489
|
+
moduleName: DEBUG_MODULE$3,
|
|
490
|
+
scope: "createAbortError"
|
|
491
|
+
};
|
|
402
492
|
const OFFICIAL_DRIVING_EXPRESSION_FALLBACKS = Object.freeze({
|
|
403
493
|
STOPPED: Object.freeze({
|
|
404
494
|
kind: "emoji",
|
|
@@ -466,7 +556,7 @@ const parseDrivingExpressionsConfig = (raw, options = {}) => {
|
|
|
466
556
|
options
|
|
467
557
|
});
|
|
468
558
|
const parsed = parseJsonObject(raw);
|
|
469
|
-
if (parsed.schemaVersion !== 1 || !isRecord
|
|
559
|
+
if (parsed.schemaVersion !== 1 || !isRecord(parsed.states, IS_RECORD_DEBUG$1)) throw new DrivingExpressionsConfigError("DRIVING_EXPRESSIONS_INVALID_SCHEMA", "drivingExpressions must use schemaVersion 1 and contain a states object");
|
|
470
560
|
const knownStatuses = new Set(CAR_RUNNING_STATUSES);
|
|
471
561
|
const stateKeys = Object.keys(parsed.states);
|
|
472
562
|
if (stateKeys.length !== CAR_RUNNING_STATUSES.length || stateKeys.some((key) => !knownStatuses.has(key))) throw new DrivingExpressionsConfigError("DRIVING_EXPRESSIONS_INCOMPLETE", "drivingExpressions.states must contain exactly the eight supported statuses");
|
|
@@ -492,6 +582,10 @@ const tryParseDrivingExpressionsConfig = (raw, options) => {
|
|
|
492
582
|
dialSdkDebugWarn(DEBUG_MODULE$3, "tryParseDrivingExpressionsConfig", "returned", { result });
|
|
493
583
|
return result;
|
|
494
584
|
} catch (error) {
|
|
585
|
+
if (!(error instanceof DrivingExpressionsConfigError)) {
|
|
586
|
+
dialSdkDebugWarn(DEBUG_MODULE$3, "tryParseDrivingExpressionsConfig", "threw unexpected error", error);
|
|
587
|
+
throw error;
|
|
588
|
+
}
|
|
495
589
|
dialSdkDebugWarn(DEBUG_MODULE$3, "tryParseDrivingExpressionsConfig", "returned null after parse error", error);
|
|
496
590
|
return null;
|
|
497
591
|
}
|
|
@@ -520,7 +614,7 @@ const readDrivingExpressionsConfig = (raw, options) => {
|
|
|
520
614
|
const waitForDrivingExpressionsConfig = (options = {}) => {
|
|
521
615
|
dialSdkDebugWarn(DEBUG_MODULE$3, "waitForDrivingExpressionsConfig", "called", { options });
|
|
522
616
|
const timeoutMs = requireNonNegativeFinite("timeoutMs", options.timeoutMs ?? 3e3);
|
|
523
|
-
const pollIntervalMs = requirePositiveFinite
|
|
617
|
+
const pollIntervalMs = requirePositiveFinite("pollIntervalMs", options.pollIntervalMs ?? 100, REQUIRE_POSITIVE_FINITE_DEBUG$1);
|
|
524
618
|
const { signal } = options;
|
|
525
619
|
return new Promise((resolve, reject) => {
|
|
526
620
|
const deadlineMs = Date.now() + timeoutMs;
|
|
@@ -568,7 +662,7 @@ const waitForDrivingExpressionsConfig = (options = {}) => {
|
|
|
568
662
|
};
|
|
569
663
|
const handleAbort = () => {
|
|
570
664
|
dialSdkDebugWarn(DEBUG_MODULE$3, "waitForDrivingExpressionsConfig.handleAbort", "called");
|
|
571
|
-
fail(createAbortError$1
|
|
665
|
+
fail(createAbortError("The operation was aborted", CREATE_ABORT_ERROR_DEBUG$1));
|
|
572
666
|
};
|
|
573
667
|
const read = () => {
|
|
574
668
|
dialSdkDebugWarn(DEBUG_MODULE$3, "waitForDrivingExpressionsConfig.read", "called");
|
|
@@ -586,7 +680,13 @@ const waitForDrivingExpressionsConfig = (options = {}) => {
|
|
|
586
680
|
handleAbort();
|
|
587
681
|
return;
|
|
588
682
|
}
|
|
589
|
-
|
|
683
|
+
let config;
|
|
684
|
+
try {
|
|
685
|
+
config = read();
|
|
686
|
+
} catch (error) {
|
|
687
|
+
fail(error);
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
590
690
|
if (config !== null) {
|
|
591
691
|
finish(config);
|
|
592
692
|
return;
|
|
@@ -881,7 +981,7 @@ const parseDrivingExpressionMedia = (raw, status, options) => {
|
|
|
881
981
|
dialSdkDebugWarn(DEBUG_MODULE$3, "parseDrivingExpressionMedia", "returned legacy image", { result });
|
|
882
982
|
return result;
|
|
883
983
|
}
|
|
884
|
-
if (!isRecord$1
|
|
984
|
+
if (!isRecord(raw, IS_RECORD_DEBUG$1) || typeof raw.kind !== "string") throw invalidMedia(status, "media must be a structured object with a supported kind");
|
|
885
985
|
const mediaAssetId = parseMediaAssetId(raw.mediaAssetId, status);
|
|
886
986
|
assertFieldsAbsent(raw, status, [
|
|
887
987
|
"items",
|
|
@@ -987,7 +1087,7 @@ const parseJsonObject = (raw) => {
|
|
|
987
1087
|
});
|
|
988
1088
|
throw error;
|
|
989
1089
|
}
|
|
990
|
-
if (!isRecord$1
|
|
1090
|
+
if (!isRecord(value, IS_RECORD_DEBUG$1)) {
|
|
991
1091
|
const error = new DrivingExpressionsConfigError("DRIVING_EXPRESSIONS_INVALID_SCHEMA", "drivingExpressions must be a JSON object or JSON object string");
|
|
992
1092
|
dialSdkDebugWarn(DEBUG_MODULE$3, "parseJsonObject", "threw", error);
|
|
993
1093
|
throw error;
|
|
@@ -1004,7 +1104,7 @@ const readRuntimeDrivingExpressionsValue = () => {
|
|
|
1004
1104
|
return result;
|
|
1005
1105
|
}
|
|
1006
1106
|
const faceConfig = runtimeGlobal.__TOOOONY_FACE_CONFIG__;
|
|
1007
|
-
if (!isRecord$1
|
|
1107
|
+
if (!isRecord(faceConfig, IS_RECORD_DEBUG$1)) {
|
|
1008
1108
|
dialSdkDebugWarn(DEBUG_MODULE$3, "readRuntimeDrivingExpressionsValue", "returned", { result: void 0 });
|
|
1009
1109
|
return;
|
|
1010
1110
|
}
|
|
@@ -1013,7 +1113,7 @@ const readRuntimeDrivingExpressionsValue = () => {
|
|
|
1013
1113
|
dialSdkDebugWarn(DEBUG_MODULE$3, "readRuntimeDrivingExpressionsValue", "returned face config", { result });
|
|
1014
1114
|
return result;
|
|
1015
1115
|
}
|
|
1016
|
-
const result = isRecord
|
|
1116
|
+
const result = isRecord(faceConfig.values, IS_RECORD_DEBUG$1) ? faceConfig.values.drivingExpressions : void 0;
|
|
1017
1117
|
dialSdkDebugWarn(DEBUG_MODULE$3, "readRuntimeDrivingExpressionsValue", "returned legacy face config", { result });
|
|
1018
1118
|
return result;
|
|
1019
1119
|
};
|
|
@@ -1135,14 +1235,6 @@ const compact = (value) => {
|
|
|
1135
1235
|
dialSdkDebugWarn(DEBUG_MODULE$3, "compact", "returned", { result: value });
|
|
1136
1236
|
return value;
|
|
1137
1237
|
};
|
|
1138
|
-
const isRecord$1 = (value) => {
|
|
1139
|
-
const result = typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1140
|
-
dialSdkDebugWarn(DEBUG_MODULE$3, "isRecord", "returned", {
|
|
1141
|
-
value,
|
|
1142
|
-
result
|
|
1143
|
-
});
|
|
1144
|
-
return result;
|
|
1145
|
-
};
|
|
1146
1238
|
const requireNonNegativeFinite = (name, value) => {
|
|
1147
1239
|
dialSdkDebugWarn(DEBUG_MODULE$3, "requireNonNegativeFinite", "called", {
|
|
1148
1240
|
name,
|
|
@@ -1156,31 +1248,6 @@ const requireNonNegativeFinite = (name, value) => {
|
|
|
1156
1248
|
dialSdkDebugWarn(DEBUG_MODULE$3, "requireNonNegativeFinite", "returned", { result: value });
|
|
1157
1249
|
return value;
|
|
1158
1250
|
};
|
|
1159
|
-
const requirePositiveFinite$1 = (name, value) => {
|
|
1160
|
-
dialSdkDebugWarn(DEBUG_MODULE$3, "requirePositiveFinite", "called", {
|
|
1161
|
-
name,
|
|
1162
|
-
value
|
|
1163
|
-
});
|
|
1164
|
-
if (!Number.isFinite(value) || value <= 0) {
|
|
1165
|
-
const error = /* @__PURE__ */ new RangeError(`${name} must be a positive finite number`);
|
|
1166
|
-
dialSdkDebugWarn(DEBUG_MODULE$3, "requirePositiveFinite", "threw", error);
|
|
1167
|
-
throw error;
|
|
1168
|
-
}
|
|
1169
|
-
dialSdkDebugWarn(DEBUG_MODULE$3, "requirePositiveFinite", "returned", { result: value });
|
|
1170
|
-
return value;
|
|
1171
|
-
};
|
|
1172
|
-
const createAbortError$1 = () => {
|
|
1173
|
-
dialSdkDebugWarn(DEBUG_MODULE$3, "createAbortError", "called");
|
|
1174
|
-
if (typeof DOMException === "function") {
|
|
1175
|
-
const result = new DOMException("The operation was aborted", "AbortError");
|
|
1176
|
-
dialSdkDebugWarn(DEBUG_MODULE$3, "createAbortError", "returned DOMException", { result });
|
|
1177
|
-
return result;
|
|
1178
|
-
}
|
|
1179
|
-
const error = /* @__PURE__ */ new Error("The operation was aborted");
|
|
1180
|
-
error.name = "AbortError";
|
|
1181
|
-
dialSdkDebugWarn(DEBUG_MODULE$3, "createAbortError", "returned Error", { result: error });
|
|
1182
|
-
return error;
|
|
1183
|
-
};
|
|
1184
1251
|
//#endregion
|
|
1185
1252
|
//#region src/sensor/page_lifecycle.ts
|
|
1186
1253
|
const DEBUG_MODULE$2 = "page_lifecycle.ts";
|
|
@@ -1315,12 +1382,9 @@ const dispatchResume = () => {
|
|
|
1315
1382
|
};
|
|
1316
1383
|
const safeInvoke = (callback) => {
|
|
1317
1384
|
dialSdkDebugWarn(DEBUG_MODULE$2, "safeInvoke", "called");
|
|
1318
|
-
|
|
1319
|
-
callback();
|
|
1320
|
-
dialSdkDebugWarn(DEBUG_MODULE$2, "safeInvoke", "returned");
|
|
1321
|
-
} catch (error) {
|
|
1385
|
+
if (invokeSafely(callback, [], (error) => {
|
|
1322
1386
|
dialSdkDebugWarn(DEBUG_MODULE$2, "safeInvoke", "callback threw", error);
|
|
1323
|
-
}
|
|
1387
|
+
})) dialSdkDebugWarn(DEBUG_MODULE$2, "safeInvoke", "returned");
|
|
1324
1388
|
};
|
|
1325
1389
|
//#endregion
|
|
1326
1390
|
//#region src/sensor/driving_status_controller.ts
|
|
@@ -1330,6 +1394,10 @@ const ZERO_VECTOR = {
|
|
|
1330
1394
|
y: 0,
|
|
1331
1395
|
z: 0
|
|
1332
1396
|
};
|
|
1397
|
+
const REQUIRE_POSITIVE_FINITE_DEBUG = {
|
|
1398
|
+
moduleName: DEBUG_MODULE$1,
|
|
1399
|
+
scope: "requirePositiveFinite"
|
|
1400
|
+
};
|
|
1333
1401
|
const createDrivingStatusController = (options = {}) => {
|
|
1334
1402
|
dialSdkDebugWarn(DEBUG_MODULE$1, "createDrivingStatusController", "called", { options });
|
|
1335
1403
|
const result = new DefaultDrivingStatusController(options);
|
|
@@ -1363,6 +1431,18 @@ var DefaultDrivingStatusController = class {
|
|
|
1363
1431
|
this.turnStatus = null;
|
|
1364
1432
|
this.turnQuietSinceMs = null;
|
|
1365
1433
|
this.oppositeTurnSinceMs = null;
|
|
1434
|
+
this.detectorCandidates = {
|
|
1435
|
+
base: null,
|
|
1436
|
+
rapid_longitudinal: null,
|
|
1437
|
+
longitudinal: null,
|
|
1438
|
+
turn: null
|
|
1439
|
+
};
|
|
1440
|
+
this.detectorRejections = {
|
|
1441
|
+
base: null,
|
|
1442
|
+
rapid_longitudinal: null,
|
|
1443
|
+
longitudinal: null,
|
|
1444
|
+
turn: null
|
|
1445
|
+
};
|
|
1366
1446
|
this.requestedRunning = false;
|
|
1367
1447
|
this.polling = false;
|
|
1368
1448
|
this.lifecycleSuspended = false;
|
|
@@ -1401,9 +1481,9 @@ var DefaultDrivingStatusController = class {
|
|
|
1401
1481
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.handleLifecycleResume", "returned");
|
|
1402
1482
|
};
|
|
1403
1483
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.constructor", "called", { options });
|
|
1404
|
-
this.sensorProvider = options.sensorProvider ??
|
|
1405
|
-
this.pollIntervalMs = requirePositiveFinite("pollIntervalMs", options.pollIntervalMs ?? 200);
|
|
1406
|
-
this.unavailableFallbackMs = requirePositiveFinite("unavailableFallbackMs", options.unavailableFallbackMs ?? 3e3);
|
|
1484
|
+
this.sensorProvider = options.sensorProvider ?? unifiedSensorInfo;
|
|
1485
|
+
this.pollIntervalMs = requirePositiveFinite("pollIntervalMs", options.pollIntervalMs ?? 200, REQUIRE_POSITIVE_FINITE_DEBUG);
|
|
1486
|
+
this.unavailableFallbackMs = requirePositiveFinite("unavailableFallbackMs", options.unavailableFallbackMs ?? 3e3, REQUIRE_POSITIVE_FINITE_DEBUG);
|
|
1407
1487
|
this.tuning = resolveDrivingStatusTuning(options.tuning);
|
|
1408
1488
|
this.clock = options.clock ?? systemClock;
|
|
1409
1489
|
this.onDiagnostic = options.onDiagnostic;
|
|
@@ -1567,25 +1647,26 @@ var DefaultDrivingStatusController = class {
|
|
|
1567
1647
|
});
|
|
1568
1648
|
return;
|
|
1569
1649
|
}
|
|
1570
|
-
const gravity = this.
|
|
1650
|
+
const gravity = this.resolveGravityFrame(payload);
|
|
1571
1651
|
const metrics = gravity === null ? null : extractDrivingSnapshotMetrics(payload, this.tuning, {
|
|
1572
|
-
filteredGravity: gravity,
|
|
1652
|
+
filteredGravity: gravity.value,
|
|
1653
|
+
filteredGravitySampledAtMs: gravity.sampledAtMs,
|
|
1573
1654
|
linearAccelerationBias: this.linearAccelerationBias,
|
|
1574
1655
|
gyroscopeBias: this.gyroscopeBias
|
|
1575
1656
|
});
|
|
1576
|
-
const accelerometerSample =
|
|
1577
|
-
if (metrics === null || accelerometerSample === null) {
|
|
1657
|
+
const accelerometerSample = extractFreshVector(payload.accelerometer, payload.capturedAtMs, this.tuning.maxSampleAgeMs);
|
|
1658
|
+
if (gravity === null || metrics === null || accelerometerSample === null) {
|
|
1578
1659
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "invalid or stale snapshot", {
|
|
1579
1660
|
metrics,
|
|
1580
1661
|
accelerometerSample
|
|
1581
1662
|
});
|
|
1582
1663
|
this.processUnavailable("invalid_or_stale_snapshot");
|
|
1583
1664
|
} else {
|
|
1584
|
-
this.processAvailable(metrics, accelerometerSample);
|
|
1665
|
+
this.processAvailable(metrics, accelerometerSample, gravity);
|
|
1585
1666
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "processed sample", { capturedAtMs: metrics.capturedAtMs });
|
|
1586
1667
|
}
|
|
1587
1668
|
} catch (error) {
|
|
1588
|
-
if (!this.polling || generation !== this.generation || pendingRead.signal.aborted || isAbortError
|
|
1669
|
+
if (!this.polling || generation !== this.generation || pendingRead.signal.aborted || isAbortError(error)) {
|
|
1589
1670
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "ignored provider error", { error });
|
|
1590
1671
|
return;
|
|
1591
1672
|
}
|
|
@@ -1600,21 +1681,44 @@ var DefaultDrivingStatusController = class {
|
|
|
1600
1681
|
});
|
|
1601
1682
|
}
|
|
1602
1683
|
}
|
|
1603
|
-
|
|
1604
|
-
const sample =
|
|
1605
|
-
if (sample === null)
|
|
1606
|
-
|
|
1684
|
+
resolveGravityFrame(info) {
|
|
1685
|
+
const sample = extractFreshVector(info.gravity, info.capturedAtMs, this.tuning.maxSampleAgeMs);
|
|
1686
|
+
if (sample === null) {
|
|
1687
|
+
if (this.filteredGravity === null || this.lastGravitySampledAtMs === null) return null;
|
|
1688
|
+
const ageMs = info.capturedAtMs - this.lastGravitySampledAtMs;
|
|
1689
|
+
return ageMs >= 0 && ageMs <= this.tuning.maxSampleAgeMs ? {
|
|
1690
|
+
value: this.filteredGravity,
|
|
1691
|
+
sampledAtMs: this.lastGravitySampledAtMs,
|
|
1692
|
+
source: "cached"
|
|
1693
|
+
} : null;
|
|
1694
|
+
}
|
|
1695
|
+
if (sample.sampledAtMs === this.lastGravitySampledAtMs && this.filteredGravity !== null) return {
|
|
1696
|
+
value: this.filteredGravity,
|
|
1697
|
+
sampledAtMs: sample.sampledAtMs,
|
|
1698
|
+
source: "live"
|
|
1699
|
+
};
|
|
1607
1700
|
if (this.filteredGravity === null || this.lastGravitySampledAtMs === null) this.filteredGravity = { ...sample.value };
|
|
1608
1701
|
else {
|
|
1609
|
-
if (sample.sampledAtMs < this.lastGravitySampledAtMs)
|
|
1702
|
+
if (sample.sampledAtMs < this.lastGravitySampledAtMs) {
|
|
1703
|
+
const ageMs = info.capturedAtMs - this.lastGravitySampledAtMs;
|
|
1704
|
+
return ageMs >= 0 && ageMs <= this.tuning.maxSampleAgeMs ? {
|
|
1705
|
+
value: this.filteredGravity,
|
|
1706
|
+
sampledAtMs: this.lastGravitySampledAtMs,
|
|
1707
|
+
source: "cached"
|
|
1708
|
+
} : null;
|
|
1709
|
+
}
|
|
1610
1710
|
const elapsedMs = Math.max(0, sample.sampledAtMs - this.lastGravitySampledAtMs);
|
|
1611
1711
|
const alpha = 1 - Math.exp(-elapsedMs / this.tuning.gravityFilterTimeConstantMs);
|
|
1612
1712
|
this.filteredGravity = lerpVector(this.filteredGravity, sample.value, alpha);
|
|
1613
1713
|
}
|
|
1614
1714
|
this.lastGravitySampledAtMs = sample.sampledAtMs;
|
|
1615
|
-
return
|
|
1715
|
+
return {
|
|
1716
|
+
value: this.filteredGravity,
|
|
1717
|
+
sampledAtMs: sample.sampledAtMs,
|
|
1718
|
+
source: "live"
|
|
1719
|
+
};
|
|
1616
1720
|
}
|
|
1617
|
-
processAvailable(metrics, accelerometerSample) {
|
|
1721
|
+
processAvailable(metrics, accelerometerSample, gravity) {
|
|
1618
1722
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.processAvailable", "called", {
|
|
1619
1723
|
metrics,
|
|
1620
1724
|
accelerometerSample
|
|
@@ -1627,6 +1731,8 @@ var DefaultDrivingStatusController = class {
|
|
|
1627
1731
|
type: "sample_available",
|
|
1628
1732
|
atMs: now,
|
|
1629
1733
|
capturedAtMs: metrics.capturedAtMs,
|
|
1734
|
+
frameSource: gravity.source,
|
|
1735
|
+
frameAgeMs: metrics.capturedAtMs - gravity.sampledAtMs,
|
|
1630
1736
|
tuningVersion: this.tuning.version
|
|
1631
1737
|
});
|
|
1632
1738
|
const addedLongitudinal = pushUniqueMetric(this.longitudinalWindow, metrics, "linearAccelerationSampledAtMs", Math.max(this.tuning.longitudinalWindowMs, this.tuning.rapidLongitudinalWindowMs));
|
|
@@ -1637,6 +1743,10 @@ var DefaultDrivingStatusController = class {
|
|
|
1637
1743
|
return;
|
|
1638
1744
|
}
|
|
1639
1745
|
const motionMad = calculateCombinedAxisMad(this.motionWindow, this.tuning.motionMinimumSamples);
|
|
1746
|
+
const previousBaseStatus = this.baseStatus;
|
|
1747
|
+
const previousUrgentStatus = this.urgentStatus;
|
|
1748
|
+
const previousLongitudinalStatus = this.normalLongitudinalStatus;
|
|
1749
|
+
const previousTurnStatus = this.turnStatus;
|
|
1640
1750
|
const baseCondition = this.updateBaseDetector(motionMad, metrics, accelerometerSample.sampledAtMs, addedMotion);
|
|
1641
1751
|
const longitudinalEvidence = detectLongitudinalEvidence(this.longitudinalWindow, this.tuning);
|
|
1642
1752
|
if (addedLongitudinal) this.updateLongitudinalDetector(longitudinalEvidence, metrics.linearAccelerationSampledAtMs);
|
|
@@ -1648,6 +1758,27 @@ var DefaultDrivingStatusController = class {
|
|
|
1648
1758
|
this.stationaryConfirmed = false;
|
|
1649
1759
|
}
|
|
1650
1760
|
if (this.stationaryConfirmed) this.updateBias(metrics);
|
|
1761
|
+
const statistics = {
|
|
1762
|
+
base: {
|
|
1763
|
+
sampleCount: this.motionWindow.length,
|
|
1764
|
+
durationMs: vectorWindowDuration(this.motionWindow),
|
|
1765
|
+
motionMad,
|
|
1766
|
+
gyroMagnitude: metrics.gyroMagnitude,
|
|
1767
|
+
rawGyroMagnitude: metrics.rawGyroMagnitude
|
|
1768
|
+
},
|
|
1769
|
+
longitudinal: longitudinalEvidence.normalStatistics,
|
|
1770
|
+
rapidLongitudinal: longitudinalEvidence.rapidStatistics,
|
|
1771
|
+
turn: turnEvidence.statistics
|
|
1772
|
+
};
|
|
1773
|
+
const baseCandidate = baseCondition === "stationary" ? "STOPPED" : baseCondition === "moving" ? "STEADY_DRIVING" : null;
|
|
1774
|
+
this.updateDetectorCandidate("base", baseCandidate, null, statistics, now);
|
|
1775
|
+
this.updateDetectorCandidate("rapid_longitudinal", longitudinalEvidence.rapidCandidate, longitudinalEvidence.rejectionReason, statistics, now);
|
|
1776
|
+
this.updateDetectorCandidate("longitudinal", longitudinalEvidence.normalCandidate, null, statistics, now);
|
|
1777
|
+
this.updateDetectorCandidate("turn", turnEvidence.candidate, turnEvidence.rejectionReason, statistics, now);
|
|
1778
|
+
this.emitDetectorConfirmation("base", previousBaseStatus, this.baseStatus, statistics, now);
|
|
1779
|
+
this.emitDetectorConfirmation("rapid_longitudinal", previousUrgentStatus, this.urgentStatus, statistics, now);
|
|
1780
|
+
this.emitDetectorConfirmation("longitudinal", previousLongitudinalStatus, this.normalLongitudinalStatus, statistics, now);
|
|
1781
|
+
this.emitDetectorConfirmation("turn", previousTurnStatus, this.turnStatus, statistics, now);
|
|
1651
1782
|
const pendingCandidate = instantaneousCandidate(metrics, this.tuning);
|
|
1652
1783
|
this.setCandidate(pendingCandidate === this.status ? null : pendingCandidate, observationTime(metrics));
|
|
1653
1784
|
this.emitDiagnostic({
|
|
@@ -1658,7 +1789,10 @@ var DefaultDrivingStatusController = class {
|
|
|
1658
1789
|
rapidCandidate: longitudinalEvidence.rapidCandidate,
|
|
1659
1790
|
longitudinalCandidate: longitudinalEvidence.normalCandidate,
|
|
1660
1791
|
turnCandidate: turnEvidence.candidate,
|
|
1661
|
-
rejectionReason: longitudinalEvidence.rejectionReason,
|
|
1792
|
+
rejectionReason: longitudinalEvidence.rejectionReason ?? turnEvidence.rejectionReason,
|
|
1793
|
+
frameSource: gravity.source,
|
|
1794
|
+
frameAgeMs: metrics.capturedAtMs - gravity.sampledAtMs,
|
|
1795
|
+
statistics,
|
|
1662
1796
|
tuningVersion: this.tuning.version
|
|
1663
1797
|
});
|
|
1664
1798
|
const hasAction = this.urgentStatus !== null || this.turnStatus !== null || this.normalLongitudinalStatus !== null;
|
|
@@ -1766,8 +1900,41 @@ var DefaultDrivingStatusController = class {
|
|
|
1766
1900
|
}
|
|
1767
1901
|
this.lastBiasSampledAtMs = sampledAtMs;
|
|
1768
1902
|
}
|
|
1903
|
+
updateDetectorCandidate(detector, candidate, rejectionReason, statistics, atMs) {
|
|
1904
|
+
const previousCandidate = this.detectorCandidates[detector];
|
|
1905
|
+
if (previousCandidate !== candidate) {
|
|
1906
|
+
if (previousCandidate !== null) this.emitDetectorTransition(detector, "candidate_exited", previousCandidate, null, statistics, atMs);
|
|
1907
|
+
if (candidate !== null) this.emitDetectorTransition(detector, "candidate_entered", candidate, null, statistics, atMs);
|
|
1908
|
+
this.detectorCandidates[detector] = candidate;
|
|
1909
|
+
}
|
|
1910
|
+
if (rejectionReason !== null && rejectionReason !== this.detectorRejections[detector]) this.emitDetectorTransition(detector, "candidate_rejected", null, rejectionReason, statistics, atMs);
|
|
1911
|
+
this.detectorRejections[detector] = rejectionReason;
|
|
1912
|
+
}
|
|
1913
|
+
emitDetectorConfirmation(detector, previousStatus, status, statistics, atMs) {
|
|
1914
|
+
if (status !== null && status !== previousStatus) this.emitDetectorTransition(detector, "candidate_confirmed", status, null, statistics, atMs);
|
|
1915
|
+
}
|
|
1916
|
+
emitDetectorTransition(detector, phase, status, rejectionReason, statistics, atMs) {
|
|
1917
|
+
this.emitDiagnostic({
|
|
1918
|
+
type: "detector_transition",
|
|
1919
|
+
atMs,
|
|
1920
|
+
detector,
|
|
1921
|
+
phase,
|
|
1922
|
+
status,
|
|
1923
|
+
rejectionReason,
|
|
1924
|
+
statistics,
|
|
1925
|
+
tuningVersion: this.tuning.version
|
|
1926
|
+
});
|
|
1927
|
+
}
|
|
1769
1928
|
commitSelectedStatus(now) {
|
|
1770
|
-
const selected =
|
|
1929
|
+
const selected = [
|
|
1930
|
+
this.baseStatus,
|
|
1931
|
+
this.normalLongitudinalStatus,
|
|
1932
|
+
this.turnStatus,
|
|
1933
|
+
this.urgentStatus
|
|
1934
|
+
].reduce((highest, candidate) => {
|
|
1935
|
+
if (candidate === null) return highest;
|
|
1936
|
+
return CAR_RUNNING_STATUS_PRIORITY[candidate] > CAR_RUNNING_STATUS_PRIORITY[highest] ? candidate : highest;
|
|
1937
|
+
}, this.baseStatus);
|
|
1771
1938
|
this.commit(selected, now, "candidate_confirmed");
|
|
1772
1939
|
}
|
|
1773
1940
|
processUnavailable(category) {
|
|
@@ -1825,7 +1992,12 @@ var DefaultDrivingStatusController = class {
|
|
|
1825
1992
|
reason,
|
|
1826
1993
|
atMs: now
|
|
1827
1994
|
};
|
|
1828
|
-
for (const listener of [...this.listeners])
|
|
1995
|
+
for (const listener of [...this.listeners]) invokeSafely(listener, [event], (error) => {
|
|
1996
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "safeCall", "callback threw", {
|
|
1997
|
+
error,
|
|
1998
|
+
value: event
|
|
1999
|
+
});
|
|
2000
|
+
});
|
|
1829
2001
|
this.emitDiagnostic({
|
|
1830
2002
|
type: "status_committed",
|
|
1831
2003
|
atMs: now,
|
|
@@ -1868,6 +2040,10 @@ var DefaultDrivingStatusController = class {
|
|
|
1868
2040
|
this.turnStatus = null;
|
|
1869
2041
|
this.turnQuietSinceMs = null;
|
|
1870
2042
|
this.oppositeTurnSinceMs = null;
|
|
2043
|
+
for (const detector of Object.keys(this.detectorCandidates)) {
|
|
2044
|
+
this.detectorCandidates[detector] = null;
|
|
2045
|
+
this.detectorRejections[detector] = null;
|
|
2046
|
+
}
|
|
1871
2047
|
}
|
|
1872
2048
|
resetTransientRecognition() {
|
|
1873
2049
|
this.clearRecognitionWindows();
|
|
@@ -1883,7 +2059,12 @@ var DefaultDrivingStatusController = class {
|
|
|
1883
2059
|
event,
|
|
1884
2060
|
hasListener: this.onDiagnostic !== void 0
|
|
1885
2061
|
});
|
|
1886
|
-
if (this.onDiagnostic)
|
|
2062
|
+
if (this.onDiagnostic) invokeSafely(this.onDiagnostic, [event], (error) => {
|
|
2063
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "safeCall", "callback threw", {
|
|
2064
|
+
error,
|
|
2065
|
+
value: event
|
|
2066
|
+
});
|
|
2067
|
+
});
|
|
1887
2068
|
}
|
|
1888
2069
|
attachLifecycleListeners() {
|
|
1889
2070
|
if (!this.managePageLifecycle || this.unsubscribePageLifecycle !== null) return;
|
|
@@ -1899,36 +2080,57 @@ const detectLongitudinalEvidence = (window, tuning) => {
|
|
|
1899
2080
|
const normalWindow = recentMetrics(window, tuning.longitudinalWindowMs, "linearAccelerationSampledAtMs");
|
|
1900
2081
|
let rapidCandidate = null;
|
|
1901
2082
|
let rejectionReason = null;
|
|
2083
|
+
const rapidValues = rapidWindow.map((sample) => sample.longitudinal);
|
|
2084
|
+
const rapidMean = trimmedMean(rapidValues);
|
|
2085
|
+
const rapidDirection = Math.sign(rapidMean);
|
|
2086
|
+
const rapidCoherence = signCoherence(rapidValues, rapidDirection, tuning.longitudinalExitThreshold);
|
|
2087
|
+
const rapidPeak = maximumAbsolute(rapidValues);
|
|
2088
|
+
const longitudinalRms = rootMeanSquare(rapidValues);
|
|
2089
|
+
const verticalRms = rootMeanSquare(rapidWindow.map((sample) => sample.vertical));
|
|
2090
|
+
const reversalWindow = recentMetrics(window, tuning.longitudinalReversalWindowMs, "linearAccelerationSampledAtMs");
|
|
2091
|
+
const hasReversal = containsBothSigns(reversalWindow.map((sample) => sample.longitudinal), tuning.longitudinalThreshold);
|
|
2092
|
+
const rapidStatistics = rapidWindow.length === 0 ? null : {
|
|
2093
|
+
sampleCount: rapidWindow.length,
|
|
2094
|
+
durationMs: metricWindowDuration(rapidWindow, "linearAccelerationSampledAtMs"),
|
|
2095
|
+
mean: rapidMean,
|
|
2096
|
+
peak: rapidPeak,
|
|
2097
|
+
coherence: rapidCoherence,
|
|
2098
|
+
longitudinalRms,
|
|
2099
|
+
verticalRms,
|
|
2100
|
+
hasReversal
|
|
2101
|
+
};
|
|
1902
2102
|
if (windowCovers(rapidWindow, tuning.rapidLongitudinalMinimumSamples, tuning.rapidLongitudinalWindowMs, "linearAccelerationSampledAtMs")) {
|
|
1903
|
-
|
|
1904
|
-
const direction = Math.sign(trimmedMean(values));
|
|
1905
|
-
const coherence = signCoherence(values, direction, tuning.longitudinalExitThreshold);
|
|
1906
|
-
const peak = Math.max(...values.map(Math.abs));
|
|
1907
|
-
const mean = Math.abs(trimmedMean(values));
|
|
1908
|
-
const longitudinalRms = rootMeanSquare(values);
|
|
1909
|
-
const verticalRms = rootMeanSquare(rapidWindow.map((sample) => sample.vertical));
|
|
1910
|
-
const reversalWindow = recentMetrics(window, tuning.longitudinalReversalWindowMs, "linearAccelerationSampledAtMs");
|
|
1911
|
-
const hasReversal = containsBothSigns(reversalWindow.map((sample) => sample.longitudinal), tuning.longitudinalThreshold);
|
|
1912
|
-
if (peak >= tuning.rapidLongitudinalPeakThreshold && mean >= tuning.rapidLongitudinalMeanThreshold) {
|
|
2103
|
+
if (rapidPeak >= tuning.rapidLongitudinalPeakThreshold && Math.abs(rapidMean) >= tuning.rapidLongitudinalMeanThreshold) {
|
|
1913
2104
|
if (verticalRms > longitudinalRms * tuning.verticalBumpRatio) rejectionReason = "vertical_bump";
|
|
1914
2105
|
else if (hasReversal) rejectionReason = "longitudinal_reversal";
|
|
1915
|
-
else if (
|
|
2106
|
+
else if (rapidDirection !== 0 && rapidCoherence >= tuning.rapidLongitudinalSignCoherence) rapidCandidate = rapidDirection > 0 ? "RAPID_ACCELERATION" : "SUDDEN_BRAKING";
|
|
1916
2107
|
}
|
|
1917
2108
|
}
|
|
1918
2109
|
let normalCandidate = null;
|
|
1919
2110
|
let retainedNormal = false;
|
|
2111
|
+
let normalStatistics = null;
|
|
1920
2112
|
if (normalWindow.length > 0) {
|
|
1921
2113
|
const values = normalWindow.map((sample) => sample.longitudinal);
|
|
1922
2114
|
const mean = trimmedMean(values);
|
|
1923
2115
|
const direction = Math.sign(mean);
|
|
2116
|
+
const coherence = signCoherence(values, direction, tuning.longitudinalExitThreshold);
|
|
2117
|
+
normalStatistics = {
|
|
2118
|
+
sampleCount: normalWindow.length,
|
|
2119
|
+
durationMs: metricWindowDuration(normalWindow, "linearAccelerationSampledAtMs"),
|
|
2120
|
+
mean,
|
|
2121
|
+
peak: maximumAbsolute(values),
|
|
2122
|
+
coherence
|
|
2123
|
+
};
|
|
1924
2124
|
retainedNormal = Math.abs(mean) >= tuning.longitudinalExitThreshold;
|
|
1925
|
-
if (windowCovers(normalWindow, tuning.longitudinalMinimumSamples, tuning.longitudinalWindowMs, "linearAccelerationSampledAtMs") && Math.abs(mean) >= tuning.longitudinalThreshold &&
|
|
2125
|
+
if (windowCovers(normalWindow, tuning.longitudinalMinimumSamples, tuning.longitudinalWindowMs, "linearAccelerationSampledAtMs") && Math.abs(mean) >= tuning.longitudinalThreshold && coherence >= tuning.longitudinalSignCoherence) normalCandidate = direction > 0 ? "ACCELERATION" : "BRAKING";
|
|
1926
2126
|
}
|
|
1927
2127
|
return {
|
|
1928
2128
|
rapidCandidate,
|
|
1929
2129
|
normalCandidate,
|
|
1930
2130
|
retainedNormal,
|
|
1931
|
-
rejectionReason
|
|
2131
|
+
rejectionReason,
|
|
2132
|
+
normalStatistics,
|
|
2133
|
+
rapidStatistics
|
|
1932
2134
|
};
|
|
1933
2135
|
};
|
|
1934
2136
|
const detectTurnEvidence = (window, tuning) => {
|
|
@@ -1936,20 +2138,47 @@ const detectTurnEvidence = (window, tuning) => {
|
|
|
1936
2138
|
if (!latest) return {
|
|
1937
2139
|
candidate: null,
|
|
1938
2140
|
latestYaw: 0,
|
|
1939
|
-
sampledAtMs: 0
|
|
2141
|
+
sampledAtMs: 0,
|
|
2142
|
+
rejectionReason: null,
|
|
2143
|
+
statistics: null
|
|
1940
2144
|
};
|
|
1941
2145
|
const strongWindow = recentMetrics(window, tuning.turnConfirmationMs, "gyroscopeSampledAtMs");
|
|
1942
2146
|
const strongMean = trimmedMean(strongWindow.map((sample) => sample.yaw));
|
|
1943
2147
|
const strongDirection = Math.sign(strongMean);
|
|
1944
|
-
const
|
|
2148
|
+
const strongCoherence = signCoherence(strongWindow.map((sample) => sample.yaw), strongDirection, tuning.turnExitYawThreshold);
|
|
2149
|
+
const strong = windowCovers(strongWindow, tuning.turnMinimumSamples, tuning.turnConfirmationMs, "gyroscopeSampledAtMs") && Math.abs(strongMean) >= tuning.turnYawThreshold && strongCoherence >= tuning.turnSignCoherence;
|
|
1945
2150
|
const signedAngle = integrateYaw(window);
|
|
1946
2151
|
const gentleDirection = Math.sign(signedAngle);
|
|
1947
|
-
const
|
|
1948
|
-
const
|
|
2152
|
+
const gentleCoherence = signCoherence(window.map((sample) => sample.yaw), gentleDirection, tuning.turnExitYawThreshold);
|
|
2153
|
+
const yawRms = rootMeanSquare(window.map((sample) => sample.yaw));
|
|
2154
|
+
const rawGyroRms = rootMeanSquare(window.map((sample) => sample.rawGyroMagnitude));
|
|
2155
|
+
const yawDominance = rawGyroRms <= Number.EPSILON ? 0 : Math.min(1, yawRms / rawGyroRms);
|
|
2156
|
+
const lateralRms = rootMeanSquare(window.map((sample) => sample.lateral));
|
|
2157
|
+
const verticalRms = rootMeanSquare(window.map((sample) => sample.vertical));
|
|
2158
|
+
const gentleShape = window.length >= tuning.turnMinimumSamples && Math.abs(signedAngle) >= tuning.turnIntegratedAngleThreshold && gentleCoherence >= tuning.turnSignCoherence;
|
|
2159
|
+
const verticalDominant = verticalRms > Math.max(lateralRms, tuning.turnVerticalNoiseFloor) * tuning.turnVerticalBumpRatio;
|
|
2160
|
+
const yawDominant = yawDominance >= tuning.turnYawDominanceThreshold;
|
|
2161
|
+
const laterallySupported = lateralRms >= tuning.turnLateralEvidenceThreshold;
|
|
2162
|
+
const rejectionReason = !strong && gentleShape && verticalDominant && !yawDominant ? "vertical_bump" : null;
|
|
2163
|
+
const direction = strong ? strongDirection : gentleShape && rejectionReason === null && (yawDominant || laterallySupported) ? gentleDirection : 0;
|
|
1949
2164
|
return {
|
|
1950
2165
|
candidate: direction === 0 ? null : direction > 0 ? "LEFT_TURN" : "RIGHT_TURN",
|
|
1951
2166
|
latestYaw: latest.yaw,
|
|
1952
|
-
sampledAtMs: latest.gyroscopeSampledAtMs
|
|
2167
|
+
sampledAtMs: latest.gyroscopeSampledAtMs,
|
|
2168
|
+
rejectionReason,
|
|
2169
|
+
statistics: {
|
|
2170
|
+
sampleCount: window.length,
|
|
2171
|
+
durationMs: metricWindowDuration(window, "gyroscopeSampledAtMs"),
|
|
2172
|
+
strongMean,
|
|
2173
|
+
strongCoherence,
|
|
2174
|
+
signedAngle,
|
|
2175
|
+
gentleCoherence,
|
|
2176
|
+
yawRms,
|
|
2177
|
+
rawGyroRms,
|
|
2178
|
+
yawDominance,
|
|
2179
|
+
lateralRms,
|
|
2180
|
+
verticalRms
|
|
2181
|
+
}
|
|
1953
2182
|
};
|
|
1954
2183
|
};
|
|
1955
2184
|
const instantaneousCandidate = (metrics, tuning) => {
|
|
@@ -1990,6 +2219,12 @@ const signCoherence = (values, direction, deadband) => {
|
|
|
1990
2219
|
};
|
|
1991
2220
|
const containsBothSigns = (values, threshold) => values.some((value) => value >= threshold) && values.some((value) => value <= -threshold);
|
|
1992
2221
|
const rootMeanSquare = (values) => values.length === 0 ? 0 : Math.sqrt(values.reduce((sum, value) => sum + value ** 2, 0) / values.length);
|
|
2222
|
+
const maximumAbsolute = (values) => values.length === 0 ? 0 : Math.max(...values.map(Math.abs));
|
|
2223
|
+
const metricWindowDuration = (values, timestampKey) => {
|
|
2224
|
+
const first = values[0];
|
|
2225
|
+
const last = values[values.length - 1];
|
|
2226
|
+
return first && last ? Math.max(0, last[timestampKey] - first[timestampKey]) : 0;
|
|
2227
|
+
};
|
|
1993
2228
|
const calculateCombinedAxisMad = (samples, minimumSamples) => {
|
|
1994
2229
|
if (samples.length < minimumSamples) return null;
|
|
1995
2230
|
const medianX = median(samples.map((sample) => sample.value.x));
|
|
@@ -2000,6 +2235,11 @@ const calculateCombinedAxisMad = (samples, minimumSamples) => {
|
|
|
2000
2235
|
const madZ = median(samples.map((sample) => Math.abs(sample.value.z - medianZ)));
|
|
2001
2236
|
return Math.hypot(madX, madY, madZ);
|
|
2002
2237
|
};
|
|
2238
|
+
const vectorWindowDuration = (values) => {
|
|
2239
|
+
const first = values[0];
|
|
2240
|
+
const last = values[values.length - 1];
|
|
2241
|
+
return first && last ? Math.max(0, last.sampledAtMs - first.sampledAtMs) : 0;
|
|
2242
|
+
};
|
|
2003
2243
|
const median = (values) => {
|
|
2004
2244
|
if (values.length === 0) return 0;
|
|
2005
2245
|
const sorted = [...values].sort((left, right) => left - right);
|
|
@@ -2009,7 +2249,6 @@ const median = (values) => {
|
|
|
2009
2249
|
const pushUniqueMetric = (window, metrics, timestampKey, windowMs) => {
|
|
2010
2250
|
const latest = window[window.length - 1];
|
|
2011
2251
|
if (latest && metrics[timestampKey] <= latest[timestampKey]) return false;
|
|
2012
|
-
if (window.some((value) => value[timestampKey] === metrics[timestampKey])) return false;
|
|
2013
2252
|
window.push(metrics);
|
|
2014
2253
|
const cutoffMs = metrics[timestampKey] - windowMs;
|
|
2015
2254
|
while (window[0] && window[0][timestampKey] < cutoffMs) window.shift();
|
|
@@ -2018,67 +2257,44 @@ const pushUniqueMetric = (window, metrics, timestampKey, windowMs) => {
|
|
|
2018
2257
|
const pushUniqueVector = (window, sample, windowMs) => {
|
|
2019
2258
|
const latest = window[window.length - 1];
|
|
2020
2259
|
if (latest && sample.sampledAtMs <= latest.sampledAtMs) return false;
|
|
2021
|
-
if (window.some((value) => value.sampledAtMs === sample.sampledAtMs)) return false;
|
|
2022
2260
|
window.push(sample);
|
|
2023
2261
|
const cutoffMs = sample.sampledAtMs - windowMs;
|
|
2024
2262
|
while (window[0] && window[0].sampledAtMs < cutoffMs) window.shift();
|
|
2025
2263
|
return true;
|
|
2026
2264
|
};
|
|
2027
|
-
const extractFreshVectorMetric = (metric, capturedAtMs, maxSampleAgeMs) => {
|
|
2028
|
-
if (!metric?.available || !Number.isFinite(capturedAtMs) || !Number.isFinite(metric.sampledAtMs)) return null;
|
|
2029
|
-
const ageMs = capturedAtMs - metric.sampledAtMs;
|
|
2030
|
-
if (ageMs < 0 || ageMs > maxSampleAgeMs) return null;
|
|
2031
|
-
const { x, y, z } = metric.value;
|
|
2032
|
-
return [
|
|
2033
|
-
x,
|
|
2034
|
-
y,
|
|
2035
|
-
z
|
|
2036
|
-
].every(Number.isFinite) ? {
|
|
2037
|
-
sampledAtMs: metric.sampledAtMs,
|
|
2038
|
-
value: metric.value
|
|
2039
|
-
} : null;
|
|
2040
|
-
};
|
|
2041
2265
|
const observationTime = (metrics) => Math.max(metrics.linearAccelerationSampledAtMs, metrics.gyroscopeSampledAtMs);
|
|
2042
2266
|
const lerpVector = (from, to, alpha) => ({
|
|
2043
2267
|
x: from.x + (to.x - from.x) * alpha,
|
|
2044
2268
|
y: from.y + (to.y - from.y) * alpha,
|
|
2045
2269
|
z: from.z + (to.z - from.z) * alpha
|
|
2046
2270
|
});
|
|
2047
|
-
const defaultSensorProvider = () => {
|
|
2048
|
-
const runtimeGlobal = globalThis;
|
|
2049
|
-
if (typeof runtimeGlobal.unifiedSensorInfo !== "function") return Promise.reject(/* @__PURE__ */ new Error("window.unifiedSensorInfo is unavailable"));
|
|
2050
|
-
return runtimeGlobal.unifiedSensorInfo();
|
|
2051
|
-
};
|
|
2052
2271
|
const systemClock = {
|
|
2053
2272
|
now: () => Date.now(),
|
|
2054
2273
|
setTimeout: (callback, delayMs) => globalThis.setTimeout(callback, delayMs),
|
|
2055
2274
|
clearTimeout: (handle) => globalThis.clearTimeout(handle)
|
|
2056
2275
|
};
|
|
2057
2276
|
const isBaseStatus = (status) => status === "STOPPED" || status === "STEADY_DRIVING";
|
|
2058
|
-
const isAbortError$1 = (error) => error instanceof Error && error.name === "AbortError";
|
|
2059
|
-
const requirePositiveFinite = (name, value) => {
|
|
2060
|
-
if (!Number.isFinite(value) || value <= 0) {
|
|
2061
|
-
const error = /* @__PURE__ */ new RangeError(`${name} must be a positive finite number`);
|
|
2062
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "requirePositiveFinite", "threw", error);
|
|
2063
|
-
throw error;
|
|
2064
|
-
}
|
|
2065
|
-
return value;
|
|
2066
|
-
};
|
|
2067
|
-
const safeCall = (callback, value) => {
|
|
2068
|
-
try {
|
|
2069
|
-
callback(value);
|
|
2070
|
-
} catch (error) {
|
|
2071
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "safeCall", "callback threw", {
|
|
2072
|
-
error,
|
|
2073
|
-
value
|
|
2074
|
-
});
|
|
2075
|
-
}
|
|
2076
|
-
};
|
|
2077
2277
|
const noop = () => void 0;
|
|
2078
2278
|
//#endregion
|
|
2079
2279
|
//#region src/sensor/driving_expression_player.ts
|
|
2080
2280
|
const DEBUG_MODULE = "driving_expression_player.ts";
|
|
2081
2281
|
const MAX_RETAINED_IMAGES = 3;
|
|
2282
|
+
const POSITIVE_FINITE_DEBUG = {
|
|
2283
|
+
moduleName: DEBUG_MODULE,
|
|
2284
|
+
scope: "positiveFinite"
|
|
2285
|
+
};
|
|
2286
|
+
const CREATE_ABORT_ERROR_DEBUG = {
|
|
2287
|
+
moduleName: DEBUG_MODULE,
|
|
2288
|
+
scope: "createAbortError"
|
|
2289
|
+
};
|
|
2290
|
+
const IS_ABORT_ERROR_DEBUG = {
|
|
2291
|
+
moduleName: DEBUG_MODULE,
|
|
2292
|
+
scope: "isAbortError"
|
|
2293
|
+
};
|
|
2294
|
+
const IS_RECORD_DEBUG = {
|
|
2295
|
+
moduleName: DEBUG_MODULE,
|
|
2296
|
+
scope: "isRecord"
|
|
2297
|
+
};
|
|
2082
2298
|
const createDrivingExpressionPlayer = (container, options) => {
|
|
2083
2299
|
dialSdkDebugWarn(DEBUG_MODULE, "createDrivingExpressionPlayer", "called", {
|
|
2084
2300
|
container,
|
|
@@ -2095,6 +2311,7 @@ var DefaultDrivingExpressionPlayer = class {
|
|
|
2095
2311
|
this.mediaKind = null;
|
|
2096
2312
|
this.errorCategory = null;
|
|
2097
2313
|
this.desiredMedia = null;
|
|
2314
|
+
this.desiredMediaRole = null;
|
|
2098
2315
|
this.userPaused = false;
|
|
2099
2316
|
this.lifecycleSuspended = false;
|
|
2100
2317
|
this.paused = false;
|
|
@@ -2143,15 +2360,15 @@ var DefaultDrivingExpressionPlayer = class {
|
|
|
2143
2360
|
this.document = container.ownerDocument;
|
|
2144
2361
|
this.config = options.config;
|
|
2145
2362
|
this.fallbacks = options.fallbacks;
|
|
2146
|
-
this.loadTimeoutMs =
|
|
2147
|
-
this.retryBackoffMs =
|
|
2363
|
+
this.loadTimeoutMs = requirePositiveFinite("loadTimeoutMs", options.loadTimeoutMs ?? 5e3, POSITIVE_FINITE_DEBUG);
|
|
2364
|
+
this.retryBackoffMs = requirePositiveFinite("retryBackoffMs", options.retryBackoffMs ?? 15e3, POSITIVE_FINITE_DEBUG);
|
|
2148
2365
|
this.fit = options.fit ?? "contain";
|
|
2149
2366
|
this.tgsRenderer = defaultTgsRenderer;
|
|
2150
2367
|
this.fetchImplementation = options.fetch ?? globalThis.fetch?.bind(globalThis);
|
|
2151
2368
|
this.onDiagnostic = options.onDiagnostic;
|
|
2152
|
-
this.maxTgsCompressedBytes =
|
|
2153
|
-
this.maxTgsJsonBytes =
|
|
2154
|
-
this.maxTgsLayers =
|
|
2369
|
+
this.maxTgsCompressedBytes = requirePositiveFinite("maxTgsCompressedBytes", options.maxTgsCompressedBytes ?? 2 * 1024 * 1024, POSITIVE_FINITE_DEBUG);
|
|
2370
|
+
this.maxTgsJsonBytes = requirePositiveFinite("maxTgsJsonBytes", options.maxTgsJsonBytes ?? 8 * 1024 * 1024, POSITIVE_FINITE_DEBUG);
|
|
2371
|
+
this.maxTgsLayers = requirePositiveFinite("maxTgsLayers", options.maxTgsLayers ?? 500, POSITIVE_FINITE_DEBUG);
|
|
2155
2372
|
if (options.managePageLifecycle ?? true) this.unsubscribePageLifecycle = subscribeDrivingPageLifecycle(this.handleLifecyclePause, this.handleLifecycleResume);
|
|
2156
2373
|
dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.constructor", "created", {
|
|
2157
2374
|
loadTimeoutMs: this.loadTimeoutMs,
|
|
@@ -2176,24 +2393,33 @@ var DefaultDrivingExpressionPlayer = class {
|
|
|
2176
2393
|
const operation = this.beginOperation();
|
|
2177
2394
|
const configured = this.config.states[status];
|
|
2178
2395
|
if (configured === void 0) {
|
|
2179
|
-
const fallback = this.
|
|
2180
|
-
if (fallback ===
|
|
2396
|
+
const fallback = this.resolveFallback(status);
|
|
2397
|
+
if (fallback.type === "missing") {
|
|
2398
|
+
this.desiredMedia = null;
|
|
2399
|
+
this.desiredMediaRole = null;
|
|
2181
2400
|
this.handleFailure(status, null, new DrivingExpressionResolutionError(status), "invalid_config", operation, false);
|
|
2182
2401
|
dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.show", "returned after missing config");
|
|
2183
2402
|
return;
|
|
2184
2403
|
}
|
|
2185
|
-
this.desiredMedia = fallback;
|
|
2404
|
+
this.desiredMedia = fallback.media;
|
|
2405
|
+
this.desiredMediaRole = "fallback";
|
|
2186
2406
|
if (this.paused) {
|
|
2187
2407
|
this.phase = "idle";
|
|
2188
2408
|
dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.show", "deferred fallback while paused");
|
|
2189
2409
|
return;
|
|
2190
2410
|
}
|
|
2191
|
-
|
|
2411
|
+
if (fallback.type === "unsupported") {
|
|
2412
|
+
this.handleFailure(status, fallback.media, unsupportedFallbackError(fallback.media), "invalid_config", operation, false, false);
|
|
2413
|
+
dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.show", "returned unsupported fallback");
|
|
2414
|
+
return;
|
|
2415
|
+
}
|
|
2416
|
+
this.renderFallback(status, fallback.media);
|
|
2192
2417
|
dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.show", "returned fallback", { snapshot: this.getSnapshot() });
|
|
2193
2418
|
return;
|
|
2194
2419
|
}
|
|
2195
2420
|
const media = normalizeMedia(configured);
|
|
2196
2421
|
this.desiredMedia = media;
|
|
2422
|
+
this.desiredMediaRole = "primary";
|
|
2197
2423
|
if (this.paused) {
|
|
2198
2424
|
this.phase = "idle";
|
|
2199
2425
|
dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.show", "deferred media while paused", { media });
|
|
@@ -2244,6 +2470,7 @@ var DefaultDrivingExpressionPlayer = class {
|
|
|
2244
2470
|
this.container.replaceChildren();
|
|
2245
2471
|
this.status = null;
|
|
2246
2472
|
this.desiredMedia = null;
|
|
2473
|
+
this.desiredMediaRole = null;
|
|
2247
2474
|
this.phase = "idle";
|
|
2248
2475
|
this.mediaKind = null;
|
|
2249
2476
|
this.errorCategory = null;
|
|
@@ -2312,7 +2539,12 @@ var DefaultDrivingExpressionPlayer = class {
|
|
|
2312
2539
|
}
|
|
2313
2540
|
if (this.status !== null && this.desiredMedia !== null) {
|
|
2314
2541
|
const operation = this.beginOperation();
|
|
2315
|
-
|
|
2542
|
+
if (this.desiredMediaRole === "fallback") {
|
|
2543
|
+
const status = this.status;
|
|
2544
|
+
const fallback = this.desiredMedia;
|
|
2545
|
+
if (isStaticFallback(fallback)) this.renderFallback(status, fallback);
|
|
2546
|
+
else this.handleFailure(status, fallback, unsupportedFallbackError(fallback), "invalid_config", operation, false, false);
|
|
2547
|
+
} else this.load(this.status, this.desiredMedia, operation);
|
|
2316
2548
|
}
|
|
2317
2549
|
dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.updatePauseState", "returned", this.getSnapshot());
|
|
2318
2550
|
}
|
|
@@ -2375,7 +2607,7 @@ var DefaultDrivingExpressionPlayer = class {
|
|
|
2375
2607
|
error,
|
|
2376
2608
|
operation
|
|
2377
2609
|
});
|
|
2378
|
-
if (!this.isCurrent(operation) || isAbortError(error)) {
|
|
2610
|
+
if (!this.isCurrent(operation) || isAbortError(error, IS_ABORT_ERROR_DEBUG)) {
|
|
2379
2611
|
dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.load", "ignored error");
|
|
2380
2612
|
return;
|
|
2381
2613
|
}
|
|
@@ -2596,19 +2828,37 @@ var DefaultDrivingExpressionPlayer = class {
|
|
|
2596
2828
|
status,
|
|
2597
2829
|
report
|
|
2598
2830
|
});
|
|
2599
|
-
const fallback = this.
|
|
2600
|
-
if (
|
|
2831
|
+
const fallback = this.resolveFallback(status);
|
|
2832
|
+
if (fallback.type !== "static") {
|
|
2601
2833
|
this.container.replaceChildren();
|
|
2602
|
-
dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.renderConfiguredFallback", "returned", {
|
|
2834
|
+
dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.renderConfiguredFallback", "returned", {
|
|
2835
|
+
result: false,
|
|
2836
|
+
fallbackType: fallback.type
|
|
2837
|
+
});
|
|
2603
2838
|
return false;
|
|
2604
2839
|
}
|
|
2605
|
-
this.renderFallback(status, fallback, report);
|
|
2840
|
+
this.renderFallback(status, fallback.media, report);
|
|
2606
2841
|
dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.renderConfiguredFallback", "returned", {
|
|
2607
2842
|
result: true,
|
|
2608
|
-
fallback
|
|
2843
|
+
fallback: fallback.media
|
|
2609
2844
|
});
|
|
2610
2845
|
return true;
|
|
2611
2846
|
}
|
|
2847
|
+
resolveFallback(status) {
|
|
2848
|
+
const fallback = this.fallbacks?.[status];
|
|
2849
|
+
const result = fallback === void 0 ? { type: "missing" } : isStaticFallback(fallback) ? {
|
|
2850
|
+
type: "static",
|
|
2851
|
+
media: fallback
|
|
2852
|
+
} : {
|
|
2853
|
+
type: "unsupported",
|
|
2854
|
+
media: fallback
|
|
2855
|
+
};
|
|
2856
|
+
dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.resolveFallback", "returned", {
|
|
2857
|
+
status,
|
|
2858
|
+
result
|
|
2859
|
+
});
|
|
2860
|
+
return result;
|
|
2861
|
+
}
|
|
2612
2862
|
renderFallback(status, fallback, report = true) {
|
|
2613
2863
|
dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.renderFallback", "called", {
|
|
2614
2864
|
status,
|
|
@@ -2621,11 +2871,11 @@ var DefaultDrivingExpressionPlayer = class {
|
|
|
2621
2871
|
this.errorCategory = null;
|
|
2622
2872
|
}
|
|
2623
2873
|
if (fallback.kind === "emoji") this.renderEmoji(fallback.text);
|
|
2624
|
-
else
|
|
2874
|
+
else {
|
|
2625
2875
|
const image = this.createImage();
|
|
2626
2876
|
image.src = fallback.url;
|
|
2627
2877
|
this.container.replaceChildren(image);
|
|
2628
|
-
}
|
|
2878
|
+
}
|
|
2629
2879
|
if (report) this.emitDiagnostic({
|
|
2630
2880
|
type: "media_fallback_applied",
|
|
2631
2881
|
status,
|
|
@@ -2674,14 +2924,15 @@ var DefaultDrivingExpressionPlayer = class {
|
|
|
2674
2924
|
});
|
|
2675
2925
|
dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.markReady", "returned", this.getSnapshot());
|
|
2676
2926
|
}
|
|
2677
|
-
handleFailure(status, media, error, category, operation, scheduleRetry = true) {
|
|
2927
|
+
handleFailure(status, media, error, category, operation, scheduleRetry = true, applyFallback = true) {
|
|
2678
2928
|
dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.handleFailure", "called", {
|
|
2679
2929
|
status,
|
|
2680
2930
|
media,
|
|
2681
2931
|
error,
|
|
2682
2932
|
category,
|
|
2683
2933
|
operation,
|
|
2684
|
-
scheduleRetry
|
|
2934
|
+
scheduleRetry,
|
|
2935
|
+
applyFallback
|
|
2685
2936
|
});
|
|
2686
2937
|
if (!this.isCurrent(operation) || this.failedOperation === operation) {
|
|
2687
2938
|
dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.handleFailure", "ignored");
|
|
@@ -2690,6 +2941,7 @@ var DefaultDrivingExpressionPlayer = class {
|
|
|
2690
2941
|
this.failedOperation = operation;
|
|
2691
2942
|
this.stopActiveVideo();
|
|
2692
2943
|
this.phase = "error";
|
|
2944
|
+
if (!applyFallback && media !== null) this.mediaKind = media.kind;
|
|
2693
2945
|
this.errorCategory = category;
|
|
2694
2946
|
this.emitDiagnostic({
|
|
2695
2947
|
type: "media_load_failed",
|
|
@@ -2698,8 +2950,24 @@ var DefaultDrivingExpressionPlayer = class {
|
|
|
2698
2950
|
category,
|
|
2699
2951
|
message: safeErrorMessage(error)
|
|
2700
2952
|
});
|
|
2701
|
-
|
|
2702
|
-
|
|
2953
|
+
let fallbackApplied = false;
|
|
2954
|
+
const fallback = applyFallback ? this.resolveFallback(status) : { type: "missing" };
|
|
2955
|
+
if (fallback.type === "static") {
|
|
2956
|
+
this.renderFallback(status, fallback.media);
|
|
2957
|
+
fallbackApplied = true;
|
|
2958
|
+
} else if (fallback.type === "unsupported") {
|
|
2959
|
+
this.container.replaceChildren();
|
|
2960
|
+
this.phase = "error";
|
|
2961
|
+
this.mediaKind = fallback.media.kind;
|
|
2962
|
+
this.errorCategory = "invalid_config";
|
|
2963
|
+
this.emitDiagnostic({
|
|
2964
|
+
type: "media_load_failed",
|
|
2965
|
+
status,
|
|
2966
|
+
kind: fallback.media.kind,
|
|
2967
|
+
category: "invalid_config",
|
|
2968
|
+
message: safeErrorMessage(unsupportedFallbackError(fallback.media))
|
|
2969
|
+
});
|
|
2970
|
+
} else {
|
|
2703
2971
|
this.phase = "error";
|
|
2704
2972
|
this.errorCategory = category;
|
|
2705
2973
|
}
|
|
@@ -2945,12 +3213,9 @@ var DefaultDrivingExpressionPlayer = class {
|
|
|
2945
3213
|
dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.emitDiagnostic", "returned no listener");
|
|
2946
3214
|
return;
|
|
2947
3215
|
}
|
|
2948
|
-
|
|
2949
|
-
this.onDiagnostic(event);
|
|
2950
|
-
dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.emitDiagnostic", "returned", { event });
|
|
2951
|
-
} catch (error) {
|
|
3216
|
+
if (invokeSafely(this.onDiagnostic, [event], (error) => {
|
|
2952
3217
|
dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.emitDiagnostic", "listener threw", error);
|
|
2953
|
-
}
|
|
3218
|
+
})) dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.emitDiagnostic", "returned", { event });
|
|
2954
3219
|
}
|
|
2955
3220
|
};
|
|
2956
3221
|
var MediaPlayerError = class extends Error {
|
|
@@ -2983,6 +3248,8 @@ const normalizeMedia = (media) => {
|
|
|
2983
3248
|
});
|
|
2984
3249
|
return result;
|
|
2985
3250
|
};
|
|
3251
|
+
const isStaticFallback = (media) => media.kind === "emoji" || media.kind === "image";
|
|
3252
|
+
const unsupportedFallbackError = (media) => new MediaPlayerError("invalid_config", `Fallback media kind ${media.kind} is unsupported; use emoji or image`);
|
|
2986
3253
|
const isRetainableImage = (media) => {
|
|
2987
3254
|
if (media.mimeType?.split(";", 1)[0]?.trim().toLowerCase() === "image/gif") return false;
|
|
2988
3255
|
try {
|
|
@@ -3068,7 +3335,7 @@ const waitForElementEvent = (element, successEvent, failureEvent, signal, timeou
|
|
|
3068
3335
|
const handleAbort = () => {
|
|
3069
3336
|
dialSdkDebugWarn(DEBUG_MODULE, "waitForElementEvent.handleAbort", "called");
|
|
3070
3337
|
cleanup();
|
|
3071
|
-
reject(createAbortError());
|
|
3338
|
+
reject(createAbortError("Aborted", CREATE_ABORT_ERROR_DEBUG));
|
|
3072
3339
|
};
|
|
3073
3340
|
element.addEventListener(successEvent, handleSuccess, { once: true });
|
|
3074
3341
|
element.addEventListener(failureEvent, handleFailure, { once: true });
|
|
@@ -3105,7 +3372,7 @@ const withTimeout = (promise, timeoutMs, signal) => {
|
|
|
3105
3372
|
const handleAbort = () => {
|
|
3106
3373
|
dialSdkDebugWarn(DEBUG_MODULE, "withTimeout.handleAbort", "called");
|
|
3107
3374
|
cleanup();
|
|
3108
|
-
reject(createAbortError());
|
|
3375
|
+
reject(createAbortError("Aborted", CREATE_ABORT_ERROR_DEBUG));
|
|
3109
3376
|
};
|
|
3110
3377
|
signal.addEventListener("abort", handleAbort, { once: true });
|
|
3111
3378
|
timer = globalThis.setTimeout(() => {
|
|
@@ -3132,9 +3399,9 @@ const defaultTgsRenderer = async (container, animationData, signal) => {
|
|
|
3132
3399
|
animationData,
|
|
3133
3400
|
signal
|
|
3134
3401
|
});
|
|
3135
|
-
if (signal.aborted) throw createAbortError();
|
|
3402
|
+
if (signal.aborted) throw createAbortError("Aborted", CREATE_ABORT_ERROR_DEBUG);
|
|
3136
3403
|
const module = await import("lottie-web/build/player/lottie_light.js");
|
|
3137
|
-
if (signal.aborted) throw createAbortError();
|
|
3404
|
+
if (signal.aborted) throw createAbortError("Aborted", CREATE_ABORT_ERROR_DEBUG);
|
|
3138
3405
|
const animation = module.default.loadAnimation({
|
|
3139
3406
|
animationData,
|
|
3140
3407
|
autoplay: true,
|
|
@@ -3158,7 +3425,7 @@ const defaultTgsRenderer = async (container, animationData, signal) => {
|
|
|
3158
3425
|
dialSdkDebugWarn(DEBUG_MODULE, "defaultTgsRenderer.aborted", "called");
|
|
3159
3426
|
cleanup();
|
|
3160
3427
|
animation.destroy();
|
|
3161
|
-
reject(createAbortError());
|
|
3428
|
+
reject(createAbortError("Aborted", CREATE_ABORT_ERROR_DEBUG));
|
|
3162
3429
|
};
|
|
3163
3430
|
const cleanup = () => {
|
|
3164
3431
|
dialSdkDebugWarn(DEBUG_MODULE, "defaultTgsRenderer.cleanup", "called");
|
|
@@ -3197,7 +3464,7 @@ function validateTgsAnimation(value, maxLayers) {
|
|
|
3197
3464
|
value,
|
|
3198
3465
|
maxLayers
|
|
3199
3466
|
});
|
|
3200
|
-
if (!isRecord(value) || !Array.isArray(value.layers)) throw new MediaPlayerError("decode", "TGS animation does not contain a layers array");
|
|
3467
|
+
if (!isRecord(value, IS_RECORD_DEBUG) || !Array.isArray(value.layers)) throw new MediaPlayerError("decode", "TGS animation does not contain a layers array");
|
|
3201
3468
|
if (value.layers.length > maxLayers) throw new MediaPlayerError("decode", "TGS animation has too many layers");
|
|
3202
3469
|
if (typeof value.fr === "number" && (!Number.isFinite(value.fr) || value.fr <= 0 || value.fr > 120)) throw new MediaPlayerError("decode", "TGS animation frame rate is outside the supported range");
|
|
3203
3470
|
if (typeof value.ip === "number" && typeof value.op === "number" && (value.op <= value.ip || value.op - value.ip > 3e4)) throw new MediaPlayerError("decode", "TGS animation duration is outside the supported range");
|
|
@@ -3252,11 +3519,7 @@ const resetVideo = (video) => {
|
|
|
3252
3519
|
video.pause();
|
|
3253
3520
|
video.removeAttribute("src");
|
|
3254
3521
|
video.removeAttribute("type");
|
|
3255
|
-
|
|
3256
|
-
video.load();
|
|
3257
|
-
} catch (error) {
|
|
3258
|
-
dialSdkDebugWarn(DEBUG_MODULE, "resetVideo", "load threw", error);
|
|
3259
|
-
}
|
|
3522
|
+
video.load();
|
|
3260
3523
|
dialSdkDebugWarn(DEBUG_MODULE, "resetVideo", "returned", { video });
|
|
3261
3524
|
};
|
|
3262
3525
|
const styleMediaElement = (element, fit) => {
|
|
@@ -3303,39 +3566,6 @@ const redactUrlQueries = (message) => {
|
|
|
3303
3566
|
dialSdkDebugWarn(DEBUG_MODULE, "redactUrlQueries", "returned", { result });
|
|
3304
3567
|
return result;
|
|
3305
3568
|
};
|
|
3306
|
-
const createAbortError = () => {
|
|
3307
|
-
dialSdkDebugWarn(DEBUG_MODULE, "createAbortError", "called");
|
|
3308
|
-
if (typeof DOMException !== "undefined") {
|
|
3309
|
-
const result = new DOMException("Aborted", "AbortError");
|
|
3310
|
-
dialSdkDebugWarn(DEBUG_MODULE, "createAbortError", "returned DOMException", { result });
|
|
3311
|
-
return result;
|
|
3312
|
-
}
|
|
3313
|
-
const error = /* @__PURE__ */ new Error("Aborted");
|
|
3314
|
-
error.name = "AbortError";
|
|
3315
|
-
dialSdkDebugWarn(DEBUG_MODULE, "createAbortError", "returned Error", { result: error });
|
|
3316
|
-
return error;
|
|
3317
|
-
};
|
|
3318
|
-
const isAbortError = (error) => {
|
|
3319
|
-
const result = error instanceof Error && error.name === "AbortError";
|
|
3320
|
-
dialSdkDebugWarn(DEBUG_MODULE, "isAbortError", "returned", {
|
|
3321
|
-
error,
|
|
3322
|
-
result
|
|
3323
|
-
});
|
|
3324
|
-
return result;
|
|
3325
|
-
};
|
|
3326
|
-
const positiveFinite = (name, value) => {
|
|
3327
|
-
dialSdkDebugWarn(DEBUG_MODULE, "positiveFinite", "called", {
|
|
3328
|
-
name,
|
|
3329
|
-
value
|
|
3330
|
-
});
|
|
3331
|
-
if (!Number.isFinite(value) || value <= 0) {
|
|
3332
|
-
const error = /* @__PURE__ */ new RangeError(`${name} must be a positive finite number`);
|
|
3333
|
-
dialSdkDebugWarn(DEBUG_MODULE, "positiveFinite", "threw", error);
|
|
3334
|
-
throw error;
|
|
3335
|
-
}
|
|
3336
|
-
dialSdkDebugWarn(DEBUG_MODULE, "positiveFinite", "returned", { result: value });
|
|
3337
|
-
return value;
|
|
3338
|
-
};
|
|
3339
3569
|
const safeDestroy = (handle) => {
|
|
3340
3570
|
dialSdkDebugWarn(DEBUG_MODULE, "safeDestroy", "called", { handle });
|
|
3341
3571
|
try {
|
|
@@ -3345,13 +3575,5 @@ const safeDestroy = (handle) => {
|
|
|
3345
3575
|
dialSdkDebugWarn(DEBUG_MODULE, "safeDestroy", "destroy threw", error);
|
|
3346
3576
|
}
|
|
3347
3577
|
};
|
|
3348
|
-
const isRecord = (value) => {
|
|
3349
|
-
const result = typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3350
|
-
dialSdkDebugWarn(DEBUG_MODULE, "isRecord", "returned", {
|
|
3351
|
-
value,
|
|
3352
|
-
result
|
|
3353
|
-
});
|
|
3354
|
-
return result;
|
|
3355
|
-
};
|
|
3356
3578
|
//#endregion
|
|
3357
3579
|
export { BATTERY_HEALTH_LABELS, CAR_RUNNING_LABELS, CAR_RUNNING_STATUSES, CAR_RUNNING_STATUS_PRIORITY, DEFAULT_DRIVING_STATUS_TUNING, DrivingExpressionResolutionError, DrivingExpressionsConfigError, OFFICIAL_DRIVING_EXPRESSIONS_CONFIG, OFFICIAL_DRIVING_EXPRESSION_FALLBACKS, buildVehicleFrame, classifyDrivingSnapshot, createDrivingExpressionPlayer, createDrivingStatusController, extractDrivingSnapshotMetrics, isPublicDrivingMediaUrl, parseDrivingExpressionsConfig, readDrivingExpressionsConfig, resolveDrivingExpression, resolveDrivingExpressionStrict, resolveDrivingStatusTuning, tryParseDrivingExpressionsConfig, unifiedSensorInfo, waitForDrivingExpressionsConfig };
|