@formant/data-sdk 0.0.116 → 0.0.118
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/data-sdk.es.js +1678 -1053
- package/dist/data-sdk.umd.js +26 -26
- package/dist/types/common/duration.d.ts +10 -0
- package/dist/types/data-sdk/src/App.d.ts +2 -0
- package/dist/types/data-sdk/src/cache/StoreCache.d.ts +17 -0
- package/dist/types/data-sdk/src/cache/filterDataByTime.d.ts +2 -0
- package/dist/types/data-sdk/src/cache/filterDataByType.d.ts +3 -0
- package/dist/types/data-sdk/src/cache/queryStore.d.ts +11 -0
- package/dist/types/data-sdk/src/{Fleet.d.ts → fleet.d.ts} +0 -0
- package/dist/types/data-sdk/src/main.d.ts +1 -0
- package/dist/types/data-sdk/src/utils/numericAggregateUtils.d.ts +1 -2
- package/package.json +5 -4
package/dist/data-sdk.es.js
CHANGED
|
@@ -19230,659 +19230,831 @@ const envLocal = urlParams$2.get("formant_local");
|
|
|
19230
19230
|
if (envLocal) {
|
|
19231
19231
|
FORMANT_API_URL = "https://api.formant.local";
|
|
19232
19232
|
}
|
|
19233
|
-
|
|
19234
|
-
|
|
19235
|
-
|
|
19233
|
+
function delay(ms) {
|
|
19234
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
19235
|
+
}
|
|
19236
|
+
class DataChannel {
|
|
19237
|
+
constructor(dataChannel) {
|
|
19238
|
+
__publicField(this, "ready", false);
|
|
19239
|
+
__publicField(this, "listeners", []);
|
|
19240
|
+
__publicField(this, "openListeners", []);
|
|
19241
|
+
__publicField(this, "closeListeners", []);
|
|
19242
|
+
__publicField(this, "errorListeners", []);
|
|
19243
|
+
__publicField(this, "binaryListeners", []);
|
|
19244
|
+
__publicField(this, "error");
|
|
19245
|
+
__publicField(this, "decoder", new TextDecoder());
|
|
19246
|
+
this.dataChannel = dataChannel;
|
|
19247
|
+
this.dataChannel.binaryType = "arraybuffer";
|
|
19248
|
+
this.dataChannel.onopen = () => {
|
|
19249
|
+
this.ready = true;
|
|
19250
|
+
this.openListeners.forEach((listener) => listener());
|
|
19251
|
+
};
|
|
19252
|
+
this.dataChannel.onclose = () => {
|
|
19253
|
+
this.ready = false;
|
|
19254
|
+
this.closeListeners.forEach((listener) => listener());
|
|
19255
|
+
};
|
|
19256
|
+
this.dataChannel.onerror = (e) => {
|
|
19257
|
+
console.error(e);
|
|
19258
|
+
this.error = "An error occurred in DataChannel";
|
|
19259
|
+
this.errorListeners.forEach((listener) => listener(e));
|
|
19260
|
+
};
|
|
19261
|
+
this.dataChannel.onmessage = (m) => {
|
|
19262
|
+
this.listeners.forEach((_) => {
|
|
19263
|
+
const d = new Uint8Array(m.data);
|
|
19264
|
+
const s = this.decoder.decode(d);
|
|
19265
|
+
_(s);
|
|
19266
|
+
});
|
|
19267
|
+
this.binaryListeners.forEach((_) => {
|
|
19268
|
+
_(new Uint8Array(m.data));
|
|
19269
|
+
});
|
|
19270
|
+
};
|
|
19236
19271
|
}
|
|
19237
|
-
|
|
19238
|
-
|
|
19239
|
-
if (typeof window !== "undefined") {
|
|
19240
|
-
urlParams2 = new URLSearchParams(window.location.search);
|
|
19241
|
-
}
|
|
19242
|
-
const moduleName2 = urlParams2.get("module");
|
|
19243
|
-
return moduleName2;
|
|
19272
|
+
addOpenListener(listener) {
|
|
19273
|
+
this.openListeners.push(listener);
|
|
19244
19274
|
}
|
|
19245
|
-
|
|
19246
|
-
|
|
19247
|
-
if (typeof window !== "undefined") {
|
|
19248
|
-
urlParams2 = new URLSearchParams(window.location.search);
|
|
19249
|
-
}
|
|
19250
|
-
const configurationId = urlParams2.get("configuration");
|
|
19251
|
-
if (configurationId === null || configurationId.trim() === "") {
|
|
19252
|
-
return void 0;
|
|
19253
|
-
}
|
|
19254
|
-
const response = await fetch(
|
|
19255
|
-
`${FORMANT_API_URL}/v1/admin/module-configurations/` + configurationId,
|
|
19256
|
-
{
|
|
19257
|
-
headers: {
|
|
19258
|
-
"Content-Type": "application/json",
|
|
19259
|
-
Authorization: "Bearer " + Authentication.token
|
|
19260
|
-
}
|
|
19261
|
-
}
|
|
19262
|
-
);
|
|
19263
|
-
const moduleConfiguration = await response.json();
|
|
19264
|
-
return moduleConfiguration.configuration;
|
|
19275
|
+
removeOpenListener(listener) {
|
|
19276
|
+
this.openListeners = this.openListeners.filter((_) => _ !== listener);
|
|
19265
19277
|
}
|
|
19266
|
-
|
|
19267
|
-
|
|
19278
|
+
addCloseListener(listener) {
|
|
19279
|
+
this.closeListeners.push(listener);
|
|
19268
19280
|
}
|
|
19269
|
-
|
|
19270
|
-
this.
|
|
19271
|
-
type: "go_to_time",
|
|
19272
|
-
time: date.getTime()
|
|
19273
|
-
});
|
|
19281
|
+
removeCloseListener(listener) {
|
|
19282
|
+
this.closeListeners = this.closeListeners.filter((l) => l !== listener);
|
|
19274
19283
|
}
|
|
19275
|
-
|
|
19276
|
-
this.
|
|
19277
|
-
type: "go_to_device",
|
|
19278
|
-
deviceId
|
|
19279
|
-
});
|
|
19284
|
+
addErrorListener(listener) {
|
|
19285
|
+
this.errorListeners.push(listener);
|
|
19280
19286
|
}
|
|
19281
|
-
|
|
19282
|
-
this.
|
|
19287
|
+
removeErrorListener(listener) {
|
|
19288
|
+
this.errorListeners = this.errorListeners.filter((l) => l !== listener);
|
|
19283
19289
|
}
|
|
19284
|
-
|
|
19285
|
-
|
|
19286
|
-
|
|
19287
|
-
throw new Error("No module context");
|
|
19290
|
+
async waitTilReady() {
|
|
19291
|
+
if (this.ready) {
|
|
19292
|
+
return true;
|
|
19288
19293
|
}
|
|
19289
|
-
|
|
19290
|
-
|
|
19291
|
-
|
|
19294
|
+
const p = new Promise((resolve, reject) => {
|
|
19295
|
+
let a2 = window.setInterval(() => {
|
|
19296
|
+
if (this.ready) {
|
|
19297
|
+
window.clearInterval(a2);
|
|
19298
|
+
resolve(true);
|
|
19299
|
+
}
|
|
19300
|
+
if (this.error) {
|
|
19301
|
+
reject(this.error);
|
|
19302
|
+
}
|
|
19303
|
+
}, 10);
|
|
19292
19304
|
});
|
|
19305
|
+
return p;
|
|
19293
19306
|
}
|
|
19294
|
-
|
|
19295
|
-
|
|
19296
|
-
|
|
19297
|
-
throw new Error("No module context");
|
|
19307
|
+
send(data) {
|
|
19308
|
+
if (!this.ready) {
|
|
19309
|
+
throw new Error("Connection has been closed");
|
|
19298
19310
|
}
|
|
19299
|
-
this.
|
|
19300
|
-
type: "set_module_data_time_range",
|
|
19301
|
-
module: moduleName2,
|
|
19302
|
-
before: beforeInMilliseconds,
|
|
19303
|
-
after: afterInMilliseconds || 0
|
|
19304
|
-
});
|
|
19311
|
+
this.dataChannel.send(data);
|
|
19305
19312
|
}
|
|
19306
|
-
|
|
19307
|
-
|
|
19308
|
-
|
|
19309
|
-
throw new Error("No module context");
|
|
19313
|
+
sendBinary(data) {
|
|
19314
|
+
if (!this.ready) {
|
|
19315
|
+
throw new Error("Connection has been closed");
|
|
19310
19316
|
}
|
|
19311
|
-
this.
|
|
19312
|
-
type: "refresh_auth_token",
|
|
19313
|
-
module: moduleName2
|
|
19314
|
-
});
|
|
19317
|
+
this.dataChannel.send(data);
|
|
19315
19318
|
}
|
|
19316
|
-
|
|
19317
|
-
|
|
19318
|
-
|
|
19319
|
-
|
|
19319
|
+
addListener(listener) {
|
|
19320
|
+
this.listeners.push(listener);
|
|
19321
|
+
}
|
|
19322
|
+
removeListener(listener) {
|
|
19323
|
+
const i = this.listeners.indexOf(listener);
|
|
19324
|
+
if (i === -1) {
|
|
19325
|
+
throw new Error("Could not find data channel listener to remove");
|
|
19320
19326
|
}
|
|
19321
|
-
this.
|
|
19322
|
-
|
|
19323
|
-
|
|
19324
|
-
|
|
19325
|
-
data
|
|
19326
|
-
});
|
|
19327
|
+
if (this.error) {
|
|
19328
|
+
throw new Error(this.error);
|
|
19329
|
+
}
|
|
19330
|
+
this.listeners.splice(i, 1);
|
|
19327
19331
|
}
|
|
19328
|
-
|
|
19329
|
-
|
|
19330
|
-
|
|
19331
|
-
|
|
19332
|
+
addBinaryListener(listener) {
|
|
19333
|
+
this.binaryListeners.push(listener);
|
|
19334
|
+
}
|
|
19335
|
+
removeBinaryListener(listener) {
|
|
19336
|
+
const i = this.binaryListeners.indexOf(listener);
|
|
19337
|
+
if (i === -1) {
|
|
19338
|
+
throw new Error("Could not find data channel listener to remove");
|
|
19332
19339
|
}
|
|
19333
|
-
this.
|
|
19334
|
-
|
|
19335
|
-
|
|
19336
|
-
|
|
19337
|
-
});
|
|
19340
|
+
if (this.error) {
|
|
19341
|
+
throw new Error(this.error);
|
|
19342
|
+
}
|
|
19343
|
+
this.binaryListeners.splice(i, 1);
|
|
19338
19344
|
}
|
|
19339
|
-
|
|
19340
|
-
|
|
19341
|
-
|
|
19342
|
-
|
|
19343
|
-
|
|
19344
|
-
}
|
|
19345
|
-
});
|
|
19345
|
+
}
|
|
19346
|
+
class CaptureStream {
|
|
19347
|
+
constructor(captureSession) {
|
|
19348
|
+
__publicField(this, "token");
|
|
19349
|
+
this.captureSession = captureSession;
|
|
19346
19350
|
}
|
|
19347
|
-
|
|
19348
|
-
|
|
19349
|
-
const
|
|
19350
|
-
|
|
19351
|
-
|
|
19351
|
+
async ingestJSON(value) {
|
|
19352
|
+
if (!this.token) {
|
|
19353
|
+
const result = await fetch(
|
|
19354
|
+
`${FORMANT_API_URL}/v1/admin/capture-sessions/${this.captureSession.code}/authenticate`,
|
|
19355
|
+
{
|
|
19356
|
+
method: "POST"
|
|
19357
|
+
}
|
|
19358
|
+
);
|
|
19359
|
+
const authInfo = await result.json();
|
|
19360
|
+
this.token = authInfo.token;
|
|
19361
|
+
}
|
|
19362
|
+
await fetch(`${FORMANT_API_URL}/v1/ingest`, {
|
|
19363
|
+
method: "POST",
|
|
19364
|
+
body: JSON.stringify({
|
|
19365
|
+
deviceId: this.captureSession.deviceId,
|
|
19366
|
+
name: this.captureSession.streamName,
|
|
19367
|
+
type: "json",
|
|
19368
|
+
points: [[Date.now(), JSON.stringify(value)]]
|
|
19369
|
+
}),
|
|
19370
|
+
headers: {
|
|
19371
|
+
"Content-Type": "application/json",
|
|
19372
|
+
Authorization: "Bearer " + this.token
|
|
19352
19373
|
}
|
|
19353
19374
|
});
|
|
19354
19375
|
}
|
|
19355
|
-
|
|
19356
|
-
|
|
19357
|
-
|
|
19358
|
-
|
|
19359
|
-
|
|
19360
|
-
|
|
19361
|
-
|
|
19362
|
-
|
|
19363
|
-
|
|
19364
|
-
streams: msg.streams,
|
|
19365
|
-
time: msg.time,
|
|
19366
|
-
queryRange: msg.queryRange
|
|
19376
|
+
}
|
|
19377
|
+
class Manipulator {
|
|
19378
|
+
constructor(device, config) {
|
|
19379
|
+
__publicField(this, "currentListeners", []);
|
|
19380
|
+
__publicField(this, "onRealtimeMessage", (_peerId, message) => {
|
|
19381
|
+
if (message.payload.jointState) {
|
|
19382
|
+
this.currentListeners.forEach((listener) => {
|
|
19383
|
+
if (message.payload.jointState)
|
|
19384
|
+
listener(message.payload.jointState);
|
|
19367
19385
|
});
|
|
19368
19386
|
}
|
|
19369
19387
|
});
|
|
19388
|
+
this.device = device;
|
|
19389
|
+
this.config = config;
|
|
19370
19390
|
}
|
|
19371
|
-
|
|
19372
|
-
|
|
19373
|
-
|
|
19374
|
-
|
|
19375
|
-
|
|
19376
|
-
}
|
|
19377
|
-
});
|
|
19391
|
+
async synchronize() {
|
|
19392
|
+
this.device.addRealtimeListener(this.onRealtimeMessage);
|
|
19393
|
+
this.device.startListeningToRealtimeDataStream(
|
|
19394
|
+
this.config.currentJointStateStream
|
|
19395
|
+
);
|
|
19378
19396
|
}
|
|
19379
|
-
|
|
19380
|
-
|
|
19381
|
-
|
|
19382
|
-
|
|
19383
|
-
|
|
19384
|
-
source: msg.source,
|
|
19385
|
-
data: msg.data
|
|
19386
|
-
});
|
|
19387
|
-
}
|
|
19388
|
-
});
|
|
19397
|
+
async desynchronize() {
|
|
19398
|
+
this.device.removeRealtimeListener(this.onRealtimeMessage);
|
|
19399
|
+
this.device.stopListeningToRealtimeDataStream(
|
|
19400
|
+
this.config.currentJointStateStream
|
|
19401
|
+
);
|
|
19389
19402
|
}
|
|
19390
|
-
|
|
19391
|
-
this.
|
|
19392
|
-
window.addEventListener("message", (event) => {
|
|
19393
|
-
const msg = event.data;
|
|
19394
|
-
handler(msg);
|
|
19395
|
-
});
|
|
19403
|
+
async addCurrentJointStateListener(listener) {
|
|
19404
|
+
this.currentListeners.push(listener);
|
|
19396
19405
|
}
|
|
19397
19406
|
}
|
|
19398
|
-
|
|
19399
|
-
|
|
19400
|
-
|
|
19401
|
-
|
|
19402
|
-
|
|
19403
|
-
|
|
19404
|
-
|
|
19405
|
-
|
|
19406
|
-
|
|
19407
|
-
|
|
19408
|
-
|
|
19409
|
-
|
|
19410
|
-
|
|
19411
|
-
|
|
19412
|
-
|
|
19413
|
-
|
|
19414
|
-
|
|
19415
|
-
|
|
19416
|
-
|
|
19417
|
-
|
|
19418
|
-
|
|
19419
|
-
|
|
19420
|
-
|
|
19407
|
+
class RequestDataChannel {
|
|
19408
|
+
constructor(device, channel_name, timeout) {
|
|
19409
|
+
__publicField(this, "channel");
|
|
19410
|
+
__publicField(this, "requestIdToResponseMap", /* @__PURE__ */ new Map());
|
|
19411
|
+
this.device = device;
|
|
19412
|
+
this.channel_name = channel_name;
|
|
19413
|
+
this.timeout = timeout;
|
|
19414
|
+
}
|
|
19415
|
+
addOpenListener(listener) {
|
|
19416
|
+
defined(this.channel, "channel not initalized").addOpenListener(listener);
|
|
19417
|
+
}
|
|
19418
|
+
removeOpenListener(listener) {
|
|
19419
|
+
defined(this.channel, "channel not initalized").removeOpenListener(
|
|
19420
|
+
listener
|
|
19421
|
+
);
|
|
19422
|
+
}
|
|
19423
|
+
addCloseListener(listener) {
|
|
19424
|
+
defined(this.channel, "channel not initalized").addCloseListener(listener);
|
|
19425
|
+
}
|
|
19426
|
+
removeCloseListener(listener) {
|
|
19427
|
+
defined(this.channel, "channel not initalized").removeCloseListener(
|
|
19428
|
+
listener
|
|
19429
|
+
);
|
|
19430
|
+
}
|
|
19431
|
+
addErrorListener(listener) {
|
|
19432
|
+
defined(this.channel, "channel not initalized").addErrorListener(listener);
|
|
19433
|
+
}
|
|
19434
|
+
removeErrorListener(listener) {
|
|
19435
|
+
defined(this.channel, "channel not initalized").removeErrorListener(
|
|
19436
|
+
listener
|
|
19437
|
+
);
|
|
19438
|
+
}
|
|
19439
|
+
}
|
|
19440
|
+
class BinaryRequestDataChannel extends RequestDataChannel {
|
|
19441
|
+
constructor() {
|
|
19442
|
+
super(...arguments);
|
|
19443
|
+
__publicField(this, "RESPONSE_SUCCESS_BYTE", 0);
|
|
19444
|
+
__publicField(this, "decoder", new TextDecoder());
|
|
19445
|
+
}
|
|
19446
|
+
generateBinaryId() {
|
|
19447
|
+
const id = new Uint8Array(16);
|
|
19448
|
+
for (let i = 0; i < id.length; i++) {
|
|
19449
|
+
id[i] = Math.floor(Math.random() * 256);
|
|
19421
19450
|
}
|
|
19451
|
+
return id;
|
|
19422
19452
|
}
|
|
19423
|
-
|
|
19424
|
-
|
|
19425
|
-
|
|
19426
|
-
|
|
19427
|
-
|
|
19428
|
-
if (
|
|
19429
|
-
|
|
19430
|
-
}
|
|
19431
|
-
if (tokenData["custom:organization_id"]) {
|
|
19432
|
-
_Authentication.currentOrganization = tokenData["custom:organization_id"];
|
|
19453
|
+
async initialize() {
|
|
19454
|
+
this.channel = await this.device.createCustomDataChannel(this.channel_name);
|
|
19455
|
+
this.channel.addBinaryListener((message) => {
|
|
19456
|
+
const binaryId = message.slice(0, 16);
|
|
19457
|
+
const id = binaryId.toString();
|
|
19458
|
+
if (id.length === 0) {
|
|
19459
|
+
throw new Error("Invalid response");
|
|
19433
19460
|
}
|
|
19434
|
-
|
|
19435
|
-
|
|
19461
|
+
const response = message.slice(16);
|
|
19462
|
+
if (response.length === 0) {
|
|
19463
|
+
throw new Error("Invalid response");
|
|
19436
19464
|
}
|
|
19437
|
-
if (
|
|
19438
|
-
|
|
19465
|
+
if (this.requestIdToResponseMap.has(id)) {
|
|
19466
|
+
this.requestIdToResponseMap.set(id, response);
|
|
19439
19467
|
}
|
|
19440
|
-
|
|
19441
|
-
|
|
19442
|
-
|
|
19443
|
-
|
|
19444
|
-
|
|
19445
|
-
|
|
19446
|
-
|
|
19447
|
-
|
|
19448
|
-
|
|
19468
|
+
});
|
|
19469
|
+
}
|
|
19470
|
+
async request(data) {
|
|
19471
|
+
if (!this.channel) {
|
|
19472
|
+
await this.initialize();
|
|
19473
|
+
}
|
|
19474
|
+
if (!this.channel) {
|
|
19475
|
+
throw new Error("Failed to create channel");
|
|
19476
|
+
}
|
|
19477
|
+
const { channel, requestIdToResponseMap, timeout } = this;
|
|
19478
|
+
await channel.waitTilReady();
|
|
19479
|
+
const binaryId = this.generateBinaryId();
|
|
19480
|
+
const id = binaryId.toString();
|
|
19481
|
+
requestIdToResponseMap.set(id, true);
|
|
19482
|
+
channel.sendBinary(new Uint8Array([...binaryId, ...data]));
|
|
19483
|
+
const start = new Date().getTime();
|
|
19484
|
+
while (new Date().getTime() < start + timeout) {
|
|
19485
|
+
await delay(50);
|
|
19486
|
+
if (requestIdToResponseMap.has(id)) {
|
|
19487
|
+
const response = requestIdToResponseMap.get(id);
|
|
19488
|
+
if (response !== true) {
|
|
19489
|
+
requestIdToResponseMap.delete(id);
|
|
19490
|
+
const success = response[0] === this.RESPONSE_SUCCESS_BYTE;
|
|
19491
|
+
const payload = response.slice(1);
|
|
19492
|
+
if (success) {
|
|
19493
|
+
return payload;
|
|
19494
|
+
} else {
|
|
19495
|
+
console.error({
|
|
19496
|
+
name: "AdapterError",
|
|
19497
|
+
message: this.decoder.decode(payload)
|
|
19498
|
+
});
|
|
19499
|
+
throw new Error("Binary request datachannel adapter error");
|
|
19449
19500
|
}
|
|
19450
|
-
);
|
|
19451
|
-
const data = await result.json();
|
|
19452
|
-
if (result.status !== 200) {
|
|
19453
|
-
throw new Error(data.message);
|
|
19454
19501
|
}
|
|
19455
|
-
_Authentication.currentUser = data;
|
|
19456
19502
|
}
|
|
19457
|
-
_Authentication.token = token;
|
|
19458
|
-
_Authentication.waitingForAuth.forEach((_) => _(true));
|
|
19459
|
-
} catch (e) {
|
|
19460
|
-
console.error(e);
|
|
19461
|
-
_Authentication.waitingForAuth.forEach((_) => _(false));
|
|
19462
|
-
}
|
|
19463
|
-
_Authentication.waitingForAuth = [];
|
|
19464
|
-
if (refreshToken) {
|
|
19465
|
-
_Authentication.refreshToken = refreshToken;
|
|
19466
|
-
setInterval(async () => {
|
|
19467
|
-
if (_Authentication.refreshToken) {
|
|
19468
|
-
const result = await fetch(
|
|
19469
|
-
`${FORMANT_API_URL}/v1/admin/auth/refresh`,
|
|
19470
|
-
{
|
|
19471
|
-
method: "POST",
|
|
19472
|
-
headers: {
|
|
19473
|
-
"Content-Type": "application/json"
|
|
19474
|
-
},
|
|
19475
|
-
body: JSON.stringify({
|
|
19476
|
-
refreshToken: _Authentication.refreshToken
|
|
19477
|
-
})
|
|
19478
|
-
}
|
|
19479
|
-
);
|
|
19480
|
-
const refreshData = await result.json();
|
|
19481
|
-
_Authentication.token = refreshData.authentication.accessToken;
|
|
19482
|
-
}
|
|
19483
|
-
}, 1e3 * 60 * 60);
|
|
19484
19503
|
}
|
|
19504
|
+
requestIdToResponseMap.delete(id);
|
|
19505
|
+
console.error({
|
|
19506
|
+
name: "TimeoutError",
|
|
19507
|
+
message: `Request timed out after ${timeout / 1e3} seconds`
|
|
19508
|
+
});
|
|
19509
|
+
throw new Error("Binary request data channel request timed out");
|
|
19485
19510
|
}
|
|
19486
|
-
|
|
19487
|
-
|
|
19511
|
+
}
|
|
19512
|
+
class TextRequestDataChannel extends RequestDataChannel {
|
|
19513
|
+
generateTextId() {
|
|
19514
|
+
return Math.random().toString(36).substring(2) + "-" + Math.random().toString(36).substring(2);
|
|
19488
19515
|
}
|
|
19489
|
-
|
|
19490
|
-
|
|
19516
|
+
async initialize() {
|
|
19517
|
+
this.channel = await this.device.createCustomDataChannel(this.channel_name);
|
|
19518
|
+
this.channel.addListener((message) => {
|
|
19519
|
+
const response = JSON.parse(message);
|
|
19520
|
+
const { id, data, error } = response;
|
|
19521
|
+
if (!id) {
|
|
19522
|
+
throw new Error("Invalid response");
|
|
19523
|
+
}
|
|
19524
|
+
if (!data && !error) {
|
|
19525
|
+
throw new Error("Invalid response");
|
|
19526
|
+
}
|
|
19527
|
+
if (this.requestIdToResponseMap.has(id)) {
|
|
19528
|
+
this.requestIdToResponseMap.set(id, response);
|
|
19529
|
+
}
|
|
19530
|
+
});
|
|
19491
19531
|
}
|
|
19492
|
-
|
|
19493
|
-
if (
|
|
19494
|
-
|
|
19495
|
-
} else {
|
|
19496
|
-
return new Promise((resolve) => {
|
|
19497
|
-
_Authentication.waitingForAuth.push(function(result) {
|
|
19498
|
-
resolve(result);
|
|
19499
|
-
});
|
|
19500
|
-
});
|
|
19532
|
+
async request(data) {
|
|
19533
|
+
if (!this.channel) {
|
|
19534
|
+
await this.initialize();
|
|
19501
19535
|
}
|
|
19502
|
-
|
|
19503
|
-
|
|
19504
|
-
|
|
19505
|
-
|
|
19536
|
+
if (!this.channel) {
|
|
19537
|
+
throw new Error("Failed to create channel");
|
|
19538
|
+
}
|
|
19539
|
+
const { channel, requestIdToResponseMap, timeout } = this;
|
|
19540
|
+
await channel.waitTilReady();
|
|
19541
|
+
const id = this.generateTextId();
|
|
19542
|
+
requestIdToResponseMap.set(id, true);
|
|
19543
|
+
channel.send(
|
|
19544
|
+
JSON.stringify({
|
|
19545
|
+
id,
|
|
19546
|
+
data
|
|
19547
|
+
})
|
|
19548
|
+
);
|
|
19549
|
+
const start = new Date().getTime();
|
|
19550
|
+
while (new Date().getTime() < start + timeout) {
|
|
19551
|
+
await delay(50);
|
|
19552
|
+
if (requestIdToResponseMap.has(id)) {
|
|
19553
|
+
const response = requestIdToResponseMap.get(id);
|
|
19554
|
+
if (response !== true) {
|
|
19555
|
+
requestIdToResponseMap.delete(id);
|
|
19556
|
+
const { data: data2, error } = response;
|
|
19557
|
+
if (data2) {
|
|
19558
|
+
return data2;
|
|
19559
|
+
}
|
|
19560
|
+
if (error) {
|
|
19561
|
+
console.error({
|
|
19562
|
+
name: "AdapterError",
|
|
19563
|
+
message: error
|
|
19564
|
+
});
|
|
19565
|
+
throw new Error("Text request datachannel adapter error");
|
|
19566
|
+
}
|
|
19567
|
+
}
|
|
19568
|
+
}
|
|
19569
|
+
}
|
|
19570
|
+
requestIdToResponseMap.delete(id);
|
|
19571
|
+
console.error({
|
|
19572
|
+
name: "TimeoutError",
|
|
19573
|
+
message: `Request timed out after ${timeout / 1e3} seconds`
|
|
19506
19574
|
});
|
|
19507
|
-
|
|
19508
|
-
App.refreshAuthToken();
|
|
19509
|
-
}, 1e3 * 60 * 60);
|
|
19575
|
+
throw new Error("Text request datachannel request timed out");
|
|
19510
19576
|
}
|
|
19511
|
-
};
|
|
19512
|
-
let Authentication = _Authentication;
|
|
19513
|
-
__publicField(Authentication, "token");
|
|
19514
|
-
__publicField(Authentication, "refreshToken");
|
|
19515
|
-
__publicField(Authentication, "currentUser");
|
|
19516
|
-
__publicField(Authentication, "currentOrganization");
|
|
19517
|
-
__publicField(Authentication, "isShareToken", false);
|
|
19518
|
-
__publicField(Authentication, "defaultDeviceId");
|
|
19519
|
-
__publicField(Authentication, "waitingForAuth", []);
|
|
19520
|
-
function delay(ms) {
|
|
19521
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
19522
19577
|
}
|
|
19523
|
-
|
|
19524
|
-
|
|
19525
|
-
|
|
19526
|
-
|
|
19527
|
-
|
|
19528
|
-
|
|
19529
|
-
|
|
19530
|
-
|
|
19531
|
-
__publicField(this, "
|
|
19532
|
-
__publicField(this, "
|
|
19533
|
-
this
|
|
19534
|
-
this
|
|
19535
|
-
|
|
19536
|
-
|
|
19537
|
-
|
|
19538
|
-
|
|
19539
|
-
this.
|
|
19540
|
-
this.ready = false;
|
|
19541
|
-
this.closeListeners.forEach((listener) => listener());
|
|
19542
|
-
};
|
|
19543
|
-
this.dataChannel.onerror = (e) => {
|
|
19544
|
-
console.error(e);
|
|
19545
|
-
this.error = "An error occurred in DataChannel";
|
|
19546
|
-
this.errorListeners.forEach((listener) => listener(e));
|
|
19547
|
-
};
|
|
19548
|
-
this.dataChannel.onmessage = (m) => {
|
|
19549
|
-
this.listeners.forEach((_) => {
|
|
19550
|
-
const d = new Uint8Array(m.data);
|
|
19551
|
-
const s = this.decoder.decode(d);
|
|
19552
|
-
_(s);
|
|
19553
|
-
});
|
|
19554
|
-
this.binaryListeners.forEach((_) => {
|
|
19555
|
-
_(new Uint8Array(m.data));
|
|
19556
|
-
});
|
|
19557
|
-
};
|
|
19558
|
-
}
|
|
19559
|
-
addOpenListener(listener) {
|
|
19560
|
-
this.openListeners.push(listener);
|
|
19561
|
-
}
|
|
19562
|
-
removeOpenListener(listener) {
|
|
19563
|
-
this.openListeners = this.openListeners.filter((_) => _ !== listener);
|
|
19578
|
+
const urlParams$1 = new URLSearchParams(window.location.search);
|
|
19579
|
+
const rtcClientVersion = urlParams$1.get("rtc_client");
|
|
19580
|
+
const SessionType = {
|
|
19581
|
+
Teleop: 1,
|
|
19582
|
+
Observe: 3
|
|
19583
|
+
};
|
|
19584
|
+
class Device {
|
|
19585
|
+
constructor(id, name, organizationId) {
|
|
19586
|
+
__publicField(this, "rtcClient");
|
|
19587
|
+
__publicField(this, "remoteDevicePeerId");
|
|
19588
|
+
__publicField(this, "realtimeListeners", []);
|
|
19589
|
+
__publicField(this, "handleMessage", (peerId, message) => {
|
|
19590
|
+
this.realtimeListeners.forEach((_) => _(peerId, message));
|
|
19591
|
+
});
|
|
19592
|
+
this.id = id;
|
|
19593
|
+
this.name = name;
|
|
19594
|
+
this.organizationId = organizationId;
|
|
19564
19595
|
}
|
|
19565
|
-
|
|
19566
|
-
|
|
19596
|
+
async getLatestTelemetry() {
|
|
19597
|
+
const data = await fetch(
|
|
19598
|
+
`${FORMANT_API_URL}/v1/queries/stream-current-value`,
|
|
19599
|
+
{
|
|
19600
|
+
method: "POST",
|
|
19601
|
+
body: JSON.stringify({
|
|
19602
|
+
deviceIds: [this.id]
|
|
19603
|
+
}),
|
|
19604
|
+
headers: {
|
|
19605
|
+
"Content-Type": "application/json",
|
|
19606
|
+
Authorization: "Bearer " + Authentication.token
|
|
19607
|
+
}
|
|
19608
|
+
}
|
|
19609
|
+
);
|
|
19610
|
+
const telemetry = await data.json();
|
|
19611
|
+
return telemetry.items;
|
|
19567
19612
|
}
|
|
19568
|
-
|
|
19569
|
-
|
|
19613
|
+
async getConfiguration() {
|
|
19614
|
+
let result = await fetch(`${FORMANT_API_URL}/v1/admin/devices/${this.id}`, {
|
|
19615
|
+
method: "GET",
|
|
19616
|
+
headers: {
|
|
19617
|
+
"Content-Type": "application/json",
|
|
19618
|
+
Authorization: "Bearer " + Authentication.token
|
|
19619
|
+
}
|
|
19620
|
+
});
|
|
19621
|
+
const device = await result.json();
|
|
19622
|
+
if (!device.state.reportedConfiguration) {
|
|
19623
|
+
throw new Error(
|
|
19624
|
+
"Device has no configuration, has it ever been turned on?"
|
|
19625
|
+
);
|
|
19626
|
+
}
|
|
19627
|
+
const version = device.state.reportedConfiguration.version;
|
|
19628
|
+
result = await fetch(
|
|
19629
|
+
`${FORMANT_API_URL}/v1/admin/devices/${this.id}/configurations/${version}`,
|
|
19630
|
+
{
|
|
19631
|
+
method: "GET",
|
|
19632
|
+
headers: {
|
|
19633
|
+
"Content-Type": "application/json",
|
|
19634
|
+
Authorization: "Bearer " + Authentication.token
|
|
19635
|
+
}
|
|
19636
|
+
}
|
|
19637
|
+
);
|
|
19638
|
+
const config = await result.json();
|
|
19639
|
+
return config.document;
|
|
19570
19640
|
}
|
|
19571
|
-
|
|
19572
|
-
|
|
19641
|
+
async getFileUrl(fileId) {
|
|
19642
|
+
const result = await fetch(`${FORMANT_API_URL}/v1/admin/files/query`, {
|
|
19643
|
+
method: "POST",
|
|
19644
|
+
body: JSON.stringify({
|
|
19645
|
+
fileIds: [fileId]
|
|
19646
|
+
}),
|
|
19647
|
+
headers: {
|
|
19648
|
+
"Content-Type": "application/json",
|
|
19649
|
+
Authorization: "Bearer " + Authentication.token
|
|
19650
|
+
}
|
|
19651
|
+
});
|
|
19652
|
+
const files = await result.json();
|
|
19653
|
+
return files.fileUrls;
|
|
19573
19654
|
}
|
|
19574
|
-
|
|
19575
|
-
this.
|
|
19655
|
+
getRealtimeStatus() {
|
|
19656
|
+
if (this.rtcClient && this.remoteDevicePeerId) {
|
|
19657
|
+
return this.rtcClient.getConnectionStatus(this.remoteDevicePeerId);
|
|
19658
|
+
} else {
|
|
19659
|
+
throw new Error(`Realtime connection hasn't been started for ${this.id}`);
|
|
19660
|
+
}
|
|
19576
19661
|
}
|
|
19577
|
-
|
|
19578
|
-
if (this.
|
|
19579
|
-
return
|
|
19662
|
+
getRealtimePing() {
|
|
19663
|
+
if (this.rtcClient && this.remoteDevicePeerId) {
|
|
19664
|
+
return this.rtcClient.getPing(this.remoteDevicePeerId);
|
|
19665
|
+
} else {
|
|
19666
|
+
throw new Error(`Realtime connection hasn't been started for ${this.id}`);
|
|
19580
19667
|
}
|
|
19581
|
-
|
|
19582
|
-
|
|
19583
|
-
|
|
19584
|
-
|
|
19585
|
-
|
|
19586
|
-
|
|
19587
|
-
|
|
19588
|
-
|
|
19668
|
+
}
|
|
19669
|
+
async startRealtimeConnection(sessionType) {
|
|
19670
|
+
if (!this.rtcClient) {
|
|
19671
|
+
let rtcClient;
|
|
19672
|
+
if (rtcClientVersion === "1") {
|
|
19673
|
+
rtcClient = new dist.exports.RtcClientV1({
|
|
19674
|
+
signalingClient: new dist.exports.RtcSignalingClient(
|
|
19675
|
+
FORMANT_API_URL + "/v1/signaling"
|
|
19676
|
+
),
|
|
19677
|
+
getToken: async () => defined(
|
|
19678
|
+
Authentication.token,
|
|
19679
|
+
"Realtime when user isn't authorized"
|
|
19680
|
+
),
|
|
19681
|
+
receive: this.handleMessage
|
|
19682
|
+
});
|
|
19683
|
+
} else {
|
|
19684
|
+
rtcClient = new dist.exports.RtcClient({
|
|
19685
|
+
signalingClient: new dist.exports.SignalingPromiseClient(
|
|
19686
|
+
FORMANT_API_URL,
|
|
19687
|
+
null,
|
|
19688
|
+
null
|
|
19689
|
+
),
|
|
19690
|
+
getToken: async () => {
|
|
19691
|
+
return defined(
|
|
19692
|
+
Authentication.token,
|
|
19693
|
+
"Realtime when user isn't authorized"
|
|
19694
|
+
);
|
|
19695
|
+
},
|
|
19696
|
+
receive: this.handleMessage,
|
|
19697
|
+
sessionType
|
|
19698
|
+
});
|
|
19699
|
+
}
|
|
19700
|
+
if (rtcClient.isReady) {
|
|
19701
|
+
while (!rtcClient.isReady()) {
|
|
19702
|
+
await delay(100);
|
|
19589
19703
|
}
|
|
19590
|
-
}
|
|
19591
|
-
|
|
19592
|
-
|
|
19593
|
-
|
|
19594
|
-
|
|
19595
|
-
|
|
19596
|
-
|
|
19704
|
+
}
|
|
19705
|
+
const peers = await rtcClient.getPeers();
|
|
19706
|
+
const devicePeer = peers.find((_) => _.deviceId === this.id);
|
|
19707
|
+
if (!devicePeer) {
|
|
19708
|
+
throw new Error("Cannot find peer, is the robot offline?");
|
|
19709
|
+
}
|
|
19710
|
+
this.remoteDevicePeerId = devicePeer.id;
|
|
19711
|
+
await rtcClient.connect(this.remoteDevicePeerId);
|
|
19712
|
+
while (rtcClient.getConnectionStatus(this.remoteDevicePeerId) !== "connected") {
|
|
19713
|
+
await delay(100);
|
|
19714
|
+
}
|
|
19715
|
+
this.rtcClient = rtcClient;
|
|
19716
|
+
} else {
|
|
19717
|
+
throw new Error(
|
|
19718
|
+
`Already created realtime connection to device ${this.id}`
|
|
19719
|
+
);
|
|
19597
19720
|
}
|
|
19598
|
-
this.dataChannel.send(data);
|
|
19599
19721
|
}
|
|
19600
|
-
|
|
19601
|
-
|
|
19602
|
-
|
|
19603
|
-
|
|
19604
|
-
|
|
19722
|
+
async sendRealtimeMessage(message, config = {
|
|
19723
|
+
channelLabel: "stream.reliable"
|
|
19724
|
+
}) {
|
|
19725
|
+
const client = defined(
|
|
19726
|
+
this.rtcClient,
|
|
19727
|
+
"Realtime connection has not been started"
|
|
19728
|
+
);
|
|
19729
|
+
const devicePeer = await this.getRemotePeer();
|
|
19730
|
+
client.send(defined(devicePeer).id, message, config);
|
|
19605
19731
|
}
|
|
19606
|
-
|
|
19607
|
-
this.
|
|
19732
|
+
addRealtimeListener(listener) {
|
|
19733
|
+
this.realtimeListeners.push(listener);
|
|
19608
19734
|
}
|
|
19609
|
-
|
|
19610
|
-
const i = this.
|
|
19735
|
+
removeRealtimeListener(listener) {
|
|
19736
|
+
const i = this.realtimeListeners.indexOf(listener);
|
|
19611
19737
|
if (i === -1) {
|
|
19612
|
-
throw new Error("Could not find
|
|
19613
|
-
}
|
|
19614
|
-
if (this.error) {
|
|
19615
|
-
throw new Error(this.error);
|
|
19738
|
+
throw new Error("Could not find realtime listener to remove");
|
|
19616
19739
|
}
|
|
19617
|
-
this.
|
|
19618
|
-
}
|
|
19619
|
-
addBinaryListener(listener) {
|
|
19620
|
-
this.binaryListeners.push(listener);
|
|
19740
|
+
this.realtimeListeners.splice(i, 1);
|
|
19621
19741
|
}
|
|
19622
|
-
|
|
19623
|
-
|
|
19624
|
-
|
|
19625
|
-
|
|
19742
|
+
async getRealtimeAudioStreams() {
|
|
19743
|
+
var _a, _b, _c, _d, _e, _f;
|
|
19744
|
+
const document2 = await this.getConfiguration();
|
|
19745
|
+
const streams = [];
|
|
19746
|
+
for (const _ of (_b = (_a = document2.teleop) == null ? void 0 : _a.hardwareStreams) != null ? _b : []) {
|
|
19747
|
+
if (_.rtcStreamType === "audio-chunk") {
|
|
19748
|
+
streams.push({
|
|
19749
|
+
name: _.name
|
|
19750
|
+
});
|
|
19751
|
+
}
|
|
19626
19752
|
}
|
|
19627
|
-
|
|
19628
|
-
|
|
19753
|
+
for (const _ of (_d = (_c = document2.teleop) == null ? void 0 : _c.rosStreams) != null ? _d : []) {
|
|
19754
|
+
if (_.topicType == "audio_common_msgs/AudioData") {
|
|
19755
|
+
streams.push({
|
|
19756
|
+
name: _.topicName
|
|
19757
|
+
});
|
|
19758
|
+
}
|
|
19629
19759
|
}
|
|
19630
|
-
|
|
19631
|
-
|
|
19632
|
-
|
|
19633
|
-
|
|
19634
|
-
|
|
19635
|
-
|
|
19636
|
-
|
|
19760
|
+
for (const _ of (_f = (_e = document2.teleop) == null ? void 0 : _e.customStreams) != null ? _f : []) {
|
|
19761
|
+
if (_.rtcStreamType === "audio-chunk") {
|
|
19762
|
+
streams.push({
|
|
19763
|
+
name: _.name
|
|
19764
|
+
});
|
|
19765
|
+
}
|
|
19766
|
+
}
|
|
19767
|
+
return streams;
|
|
19637
19768
|
}
|
|
19638
|
-
async
|
|
19639
|
-
|
|
19640
|
-
|
|
19641
|
-
|
|
19642
|
-
|
|
19643
|
-
|
|
19644
|
-
|
|
19645
|
-
|
|
19646
|
-
|
|
19647
|
-
|
|
19769
|
+
async getRealtimeVideoStreams() {
|
|
19770
|
+
var _a, _b, _c, _d, _e, _f;
|
|
19771
|
+
const document2 = await this.getConfiguration();
|
|
19772
|
+
const streams = [];
|
|
19773
|
+
for (const _ of (_b = (_a = document2.teleop) == null ? void 0 : _a.hardwareStreams) != null ? _b : []) {
|
|
19774
|
+
if (_.rtcStreamType === "h264-video-frame") {
|
|
19775
|
+
streams.push({
|
|
19776
|
+
name: _.name
|
|
19777
|
+
});
|
|
19778
|
+
}
|
|
19648
19779
|
}
|
|
19649
|
-
|
|
19650
|
-
|
|
19651
|
-
|
|
19652
|
-
|
|
19653
|
-
|
|
19654
|
-
type: "json",
|
|
19655
|
-
points: [[Date.now(), JSON.stringify(value)]]
|
|
19656
|
-
}),
|
|
19657
|
-
headers: {
|
|
19658
|
-
"Content-Type": "application/json",
|
|
19659
|
-
Authorization: "Bearer " + this.token
|
|
19780
|
+
for (const _ of (_d = (_c = document2.teleop) == null ? void 0 : _c.rosStreams) != null ? _d : []) {
|
|
19781
|
+
if (_.topicType == "formant/H264VideoFrame") {
|
|
19782
|
+
streams.push({
|
|
19783
|
+
name: _.topicName
|
|
19784
|
+
});
|
|
19660
19785
|
}
|
|
19661
|
-
}
|
|
19662
|
-
|
|
19663
|
-
|
|
19664
|
-
|
|
19665
|
-
|
|
19666
|
-
__publicField(this, "currentListeners", []);
|
|
19667
|
-
__publicField(this, "onRealtimeMessage", (_peerId, message) => {
|
|
19668
|
-
if (message.payload.jointState) {
|
|
19669
|
-
this.currentListeners.forEach((listener) => {
|
|
19670
|
-
if (message.payload.jointState)
|
|
19671
|
-
listener(message.payload.jointState);
|
|
19786
|
+
}
|
|
19787
|
+
for (const _ of (_f = (_e = document2.teleop) == null ? void 0 : _e.customStreams) != null ? _f : []) {
|
|
19788
|
+
if (_.rtcStreamType === "h264-video-frame") {
|
|
19789
|
+
streams.push({
|
|
19790
|
+
name: _.name
|
|
19672
19791
|
});
|
|
19673
19792
|
}
|
|
19674
|
-
}
|
|
19675
|
-
|
|
19676
|
-
this.config = config;
|
|
19793
|
+
}
|
|
19794
|
+
return streams;
|
|
19677
19795
|
}
|
|
19678
|
-
async
|
|
19679
|
-
|
|
19680
|
-
this.
|
|
19681
|
-
|
|
19682
|
-
)
|
|
19796
|
+
async getRealtimeManipulators() {
|
|
19797
|
+
var _a;
|
|
19798
|
+
const document2 = await this.getConfiguration();
|
|
19799
|
+
const manipulators = [];
|
|
19800
|
+
for (const _ of (_a = document2.teleop.rosStreams) != null ? _a : []) {
|
|
19801
|
+
if (_.topicType == "sensor_msgs/JointState") {
|
|
19802
|
+
manipulators.push(
|
|
19803
|
+
new Manipulator(this, {
|
|
19804
|
+
currentJointStateStream: { name: _.topicName },
|
|
19805
|
+
plannedJointStateStream: _.plannedTopic ? { name: _.plannedTopic } : void 0,
|
|
19806
|
+
planValidStream: _.planValidTopic ? { name: _.planValidTopic } : void 0,
|
|
19807
|
+
endEffectorStream: _.endEffectorTopic ? { name: _.endEffectorTopic } : void 0,
|
|
19808
|
+
endEffectorLinkName: _.endEffectorLinkName,
|
|
19809
|
+
baseReferenceFrame: _.baseReferenceFrame,
|
|
19810
|
+
localFrame: _.localFrame
|
|
19811
|
+
})
|
|
19812
|
+
);
|
|
19813
|
+
}
|
|
19814
|
+
}
|
|
19815
|
+
return manipulators;
|
|
19683
19816
|
}
|
|
19684
|
-
async
|
|
19685
|
-
|
|
19686
|
-
|
|
19687
|
-
|
|
19817
|
+
async startListeningToRealtimeVideo(stream) {
|
|
19818
|
+
const client = defined(
|
|
19819
|
+
this.rtcClient,
|
|
19820
|
+
"Realtime connection has not been started"
|
|
19688
19821
|
);
|
|
19822
|
+
const devicePeer = await this.getRemotePeer();
|
|
19823
|
+
client.controlRemoteStream(defined(devicePeer).id, {
|
|
19824
|
+
streamName: stream.name,
|
|
19825
|
+
enable: true,
|
|
19826
|
+
pipeline: "rtc"
|
|
19827
|
+
});
|
|
19689
19828
|
}
|
|
19690
|
-
async
|
|
19691
|
-
|
|
19692
|
-
|
|
19693
|
-
|
|
19694
|
-
|
|
19695
|
-
|
|
19696
|
-
|
|
19697
|
-
|
|
19698
|
-
|
|
19699
|
-
|
|
19700
|
-
|
|
19829
|
+
async stopListeningToRealtimeVideo(stream) {
|
|
19830
|
+
const client = defined(
|
|
19831
|
+
this.rtcClient,
|
|
19832
|
+
"Realtime connection has not been started"
|
|
19833
|
+
);
|
|
19834
|
+
const devicePeer = await this.getRemotePeer();
|
|
19835
|
+
client.controlRemoteStream(defined(devicePeer).id, {
|
|
19836
|
+
streamName: stream.name,
|
|
19837
|
+
enable: false,
|
|
19838
|
+
pipeline: "rtc"
|
|
19839
|
+
});
|
|
19701
19840
|
}
|
|
19702
|
-
|
|
19703
|
-
|
|
19841
|
+
async startListeningToRealtimeDataStream(stream) {
|
|
19842
|
+
const client = defined(
|
|
19843
|
+
this.rtcClient,
|
|
19844
|
+
"Realtime connection has not been started"
|
|
19845
|
+
);
|
|
19846
|
+
const devicePeer = await this.getRemotePeer();
|
|
19847
|
+
client.controlRemoteStream(defined(devicePeer).id, {
|
|
19848
|
+
streamName: stream.name,
|
|
19849
|
+
enable: true,
|
|
19850
|
+
pipeline: "rtc"
|
|
19851
|
+
});
|
|
19704
19852
|
}
|
|
19705
|
-
|
|
19706
|
-
|
|
19707
|
-
|
|
19853
|
+
async stopListeningToRealtimeDataStream(stream) {
|
|
19854
|
+
const client = defined(
|
|
19855
|
+
this.rtcClient,
|
|
19856
|
+
"Realtime connection has not been started"
|
|
19708
19857
|
);
|
|
19858
|
+
const devicePeer = await this.getRemotePeer();
|
|
19859
|
+
client.controlRemoteStream(defined(devicePeer).id, {
|
|
19860
|
+
streamName: stream.name,
|
|
19861
|
+
enable: false,
|
|
19862
|
+
pipeline: "rtc"
|
|
19863
|
+
});
|
|
19709
19864
|
}
|
|
19710
|
-
|
|
19711
|
-
|
|
19865
|
+
async enableRealtimeTelemetryPriorityIngestion(streamName) {
|
|
19866
|
+
const client = defined(
|
|
19867
|
+
this.rtcClient,
|
|
19868
|
+
"Realtime connection has not been started"
|
|
19869
|
+
);
|
|
19870
|
+
const devicePeer = await this.getRemotePeer();
|
|
19871
|
+
client.controlRemoteStream(defined(devicePeer).id, {
|
|
19872
|
+
streamName,
|
|
19873
|
+
enablePriorityUpload: true,
|
|
19874
|
+
pipeline: "telemetry"
|
|
19875
|
+
});
|
|
19712
19876
|
}
|
|
19713
|
-
|
|
19714
|
-
|
|
19715
|
-
|
|
19877
|
+
async changeStreamAudioType(streamName, newFormat) {
|
|
19878
|
+
const client = defined(
|
|
19879
|
+
this.rtcClient,
|
|
19880
|
+
"Realtime connection has not been started"
|
|
19716
19881
|
);
|
|
19882
|
+
const devicePeer = await this.getRemotePeer();
|
|
19883
|
+
client.controlRemoteStream(defined(devicePeer).id, {
|
|
19884
|
+
streamName,
|
|
19885
|
+
setAudioFormat: newFormat
|
|
19886
|
+
});
|
|
19717
19887
|
}
|
|
19718
|
-
|
|
19719
|
-
|
|
19888
|
+
async disableRealtimeTelemetryPriorityIngestion(streamName) {
|
|
19889
|
+
const client = defined(
|
|
19890
|
+
this.rtcClient,
|
|
19891
|
+
"Realtime connection has not been started"
|
|
19892
|
+
);
|
|
19893
|
+
const devicePeer = await this.getRemotePeer();
|
|
19894
|
+
client.controlRemoteStream(defined(devicePeer).id, {
|
|
19895
|
+
streamName,
|
|
19896
|
+
enablePriorityUpload: false,
|
|
19897
|
+
pipeline: "telemetry"
|
|
19898
|
+
});
|
|
19720
19899
|
}
|
|
19721
|
-
|
|
19722
|
-
|
|
19723
|
-
|
|
19900
|
+
async getRemotePeer() {
|
|
19901
|
+
const peers = await defined(
|
|
19902
|
+
this.rtcClient,
|
|
19903
|
+
"Realtime connection has not been started"
|
|
19904
|
+
).getPeers();
|
|
19905
|
+
const devicePeer = peers.find((_) => _.deviceId === this.id);
|
|
19906
|
+
return defined(
|
|
19907
|
+
devicePeer,
|
|
19908
|
+
"Could not find remote peer for device " + this.id
|
|
19724
19909
|
);
|
|
19725
19910
|
}
|
|
19726
|
-
|
|
19727
|
-
|
|
19728
|
-
|
|
19729
|
-
|
|
19730
|
-
|
|
19731
|
-
|
|
19911
|
+
async stopRealtimeConnection() {
|
|
19912
|
+
if (this.rtcClient) {
|
|
19913
|
+
await this.rtcClient.disconnect(this.id);
|
|
19914
|
+
} else {
|
|
19915
|
+
throw new Error(`Realtime connection hasn't been started for ${this.id}`);
|
|
19916
|
+
}
|
|
19732
19917
|
}
|
|
19733
|
-
|
|
19734
|
-
|
|
19735
|
-
|
|
19736
|
-
|
|
19918
|
+
async isInRealtimeSession() {
|
|
19919
|
+
let peers = await Fleet.getPeers();
|
|
19920
|
+
let sessions = await Fleet.getRealtimeSessions();
|
|
19921
|
+
let peer = peers.find((_) => _.deviceId === this.id);
|
|
19922
|
+
if (peer) {
|
|
19923
|
+
return sessions[peer.id].length > 0;
|
|
19737
19924
|
}
|
|
19738
|
-
return
|
|
19925
|
+
return false;
|
|
19739
19926
|
}
|
|
19740
|
-
async
|
|
19741
|
-
|
|
19742
|
-
|
|
19743
|
-
|
|
19744
|
-
|
|
19745
|
-
|
|
19746
|
-
|
|
19747
|
-
|
|
19748
|
-
|
|
19749
|
-
if (response.length === 0) {
|
|
19750
|
-
throw new Error("Invalid response");
|
|
19751
|
-
}
|
|
19752
|
-
if (this.requestIdToResponseMap.has(id)) {
|
|
19753
|
-
this.requestIdToResponseMap.set(id, response);
|
|
19927
|
+
async getAvailableCommands() {
|
|
19928
|
+
const result = await fetch(
|
|
19929
|
+
`${FORMANT_API_URL}/v1/admin/command-templates/`,
|
|
19930
|
+
{
|
|
19931
|
+
method: "GET",
|
|
19932
|
+
headers: {
|
|
19933
|
+
"Content-Type": "application/json",
|
|
19934
|
+
Authorization: "Bearer " + Authentication.token
|
|
19935
|
+
}
|
|
19754
19936
|
}
|
|
19755
|
-
|
|
19937
|
+
);
|
|
19938
|
+
const commands = await result.json();
|
|
19939
|
+
return commands.items.map((i) => ({
|
|
19940
|
+
name: i.name,
|
|
19941
|
+
id: i.id,
|
|
19942
|
+
command: i.command,
|
|
19943
|
+
description: i.description,
|
|
19944
|
+
parameterEnabled: i.parameterEnabled,
|
|
19945
|
+
parameterValue: i.parameterValue,
|
|
19946
|
+
parameterMeta: i.parameterMeta,
|
|
19947
|
+
enabled: i.enabled
|
|
19948
|
+
}));
|
|
19756
19949
|
}
|
|
19757
|
-
async
|
|
19758
|
-
|
|
19759
|
-
|
|
19760
|
-
|
|
19761
|
-
if (!
|
|
19762
|
-
throw new Error(
|
|
19950
|
+
async sendCommand(name, data, time, metadata) {
|
|
19951
|
+
var _a;
|
|
19952
|
+
const commands = await this.getAvailableCommands();
|
|
19953
|
+
const command = commands.find((_) => _.name === name);
|
|
19954
|
+
if (!command) {
|
|
19955
|
+
throw new Error(`Could not find command with name "${name}"`);
|
|
19763
19956
|
}
|
|
19764
|
-
|
|
19765
|
-
|
|
19766
|
-
|
|
19767
|
-
|
|
19768
|
-
|
|
19769
|
-
|
|
19770
|
-
|
|
19771
|
-
|
|
19772
|
-
await delay(50);
|
|
19773
|
-
if (requestIdToResponseMap.has(id)) {
|
|
19774
|
-
const response = requestIdToResponseMap.get(id);
|
|
19775
|
-
if (response !== true) {
|
|
19776
|
-
requestIdToResponseMap.delete(id);
|
|
19777
|
-
const success = response[0] === this.RESPONSE_SUCCESS_BYTE;
|
|
19778
|
-
const payload = response.slice(1);
|
|
19779
|
-
if (success) {
|
|
19780
|
-
return payload;
|
|
19781
|
-
} else {
|
|
19782
|
-
console.error({
|
|
19783
|
-
name: "AdapterError",
|
|
19784
|
-
message: this.decoder.decode(payload)
|
|
19785
|
-
});
|
|
19786
|
-
throw new Error("Binary request datachannel adapter error");
|
|
19787
|
-
}
|
|
19788
|
-
}
|
|
19957
|
+
let d;
|
|
19958
|
+
if (data === void 0) {
|
|
19959
|
+
if (command.parameterEnabled && command.parameterValue) {
|
|
19960
|
+
d = command.parameterValue;
|
|
19961
|
+
} else {
|
|
19962
|
+
throw new Error(
|
|
19963
|
+
"Command has no default parameter value, you must provide one"
|
|
19964
|
+
);
|
|
19789
19965
|
}
|
|
19966
|
+
} else {
|
|
19967
|
+
d = data;
|
|
19790
19968
|
}
|
|
19791
|
-
|
|
19792
|
-
|
|
19793
|
-
|
|
19794
|
-
|
|
19795
|
-
|
|
19796
|
-
|
|
19797
|
-
}
|
|
19798
|
-
}
|
|
19799
|
-
class TextRequestDataChannel extends RequestDataChannel {
|
|
19800
|
-
generateTextId() {
|
|
19801
|
-
return Math.random().toString(36).substring(2) + "-" + Math.random().toString(36).substring(2);
|
|
19802
|
-
}
|
|
19803
|
-
async initialize() {
|
|
19804
|
-
this.channel = await this.device.createCustomDataChannel(this.channel_name);
|
|
19805
|
-
this.channel.addListener((message) => {
|
|
19806
|
-
const response = JSON.parse(message);
|
|
19807
|
-
const { id, data, error } = response;
|
|
19808
|
-
if (!id) {
|
|
19809
|
-
throw new Error("Invalid response");
|
|
19810
|
-
}
|
|
19811
|
-
if (!data && !error) {
|
|
19812
|
-
throw new Error("Invalid response");
|
|
19969
|
+
let parameter = {
|
|
19970
|
+
value: d,
|
|
19971
|
+
scrubberTime: (time || new Date()).toISOString(),
|
|
19972
|
+
meta: {
|
|
19973
|
+
...command.parameterMeta,
|
|
19974
|
+
...metadata
|
|
19813
19975
|
}
|
|
19814
|
-
|
|
19815
|
-
|
|
19976
|
+
};
|
|
19977
|
+
await fetch(`${FORMANT_API_URL}/v1/admin/commands`, {
|
|
19978
|
+
method: "POST",
|
|
19979
|
+
body: JSON.stringify({
|
|
19980
|
+
commandTemplateId: command.id,
|
|
19981
|
+
organizationId: this.organizationId,
|
|
19982
|
+
deviceId: this.id,
|
|
19983
|
+
command: command.command,
|
|
19984
|
+
parameter,
|
|
19985
|
+
userId: (_a = Authentication.currentUser) == null ? void 0 : _a.id
|
|
19986
|
+
}),
|
|
19987
|
+
headers: {
|
|
19988
|
+
"Content-Type": "application/json",
|
|
19989
|
+
Authorization: "Bearer " + Authentication.token
|
|
19816
19990
|
}
|
|
19817
19991
|
});
|
|
19818
19992
|
}
|
|
19819
|
-
async
|
|
19820
|
-
if (
|
|
19821
|
-
|
|
19822
|
-
|
|
19823
|
-
|
|
19824
|
-
throw new Error("Failed to create channel");
|
|
19993
|
+
async createCustomDataChannel(channelName, rtcConfig) {
|
|
19994
|
+
if (rtcClientVersion === "1") {
|
|
19995
|
+
throw new Error(
|
|
19996
|
+
"createCustomDataChannel is not supported in rtcClientVersion 1"
|
|
19997
|
+
);
|
|
19825
19998
|
}
|
|
19826
|
-
const
|
|
19827
|
-
|
|
19828
|
-
|
|
19829
|
-
requestIdToResponseMap.set(id, true);
|
|
19830
|
-
channel.send(
|
|
19831
|
-
JSON.stringify({
|
|
19832
|
-
id,
|
|
19833
|
-
data
|
|
19834
|
-
})
|
|
19999
|
+
const client = defined(
|
|
20000
|
+
this.rtcClient,
|
|
20001
|
+
"Realtime connection has not been started"
|
|
19835
20002
|
);
|
|
19836
|
-
const
|
|
19837
|
-
|
|
19838
|
-
|
|
19839
|
-
|
|
19840
|
-
|
|
19841
|
-
|
|
19842
|
-
|
|
19843
|
-
|
|
19844
|
-
|
|
19845
|
-
|
|
19846
|
-
|
|
19847
|
-
|
|
19848
|
-
|
|
19849
|
-
name: "AdapterError",
|
|
19850
|
-
message: error
|
|
19851
|
-
});
|
|
19852
|
-
throw new Error("Text request datachannel adapter error");
|
|
19853
|
-
}
|
|
20003
|
+
const devicePeer = await this.getRemotePeer();
|
|
20004
|
+
const p = await new Promise((resolve) => {
|
|
20005
|
+
client.createCustomDataChannel(
|
|
20006
|
+
defined(devicePeer).id,
|
|
20007
|
+
channelName,
|
|
20008
|
+
{
|
|
20009
|
+
ordered: true,
|
|
20010
|
+
...rtcConfig
|
|
20011
|
+
},
|
|
20012
|
+
false,
|
|
20013
|
+
(_peerId, channel) => {
|
|
20014
|
+
const dataChannel = new DataChannel(channel);
|
|
20015
|
+
resolve(dataChannel);
|
|
19854
20016
|
}
|
|
20017
|
+
);
|
|
20018
|
+
});
|
|
20019
|
+
await p.waitTilReady();
|
|
20020
|
+
return p;
|
|
20021
|
+
}
|
|
20022
|
+
createCustomRequestDataChannel(channelName, timeout = 3e3) {
|
|
20023
|
+
return new TextRequestDataChannel(this, channelName, timeout);
|
|
20024
|
+
}
|
|
20025
|
+
createCustomBinaryRequestDataChannel(channelName, timeout = 3e3) {
|
|
20026
|
+
return new BinaryRequestDataChannel(this, channelName, timeout);
|
|
20027
|
+
}
|
|
20028
|
+
async createCaptureStream(streamName) {
|
|
20029
|
+
const result = await fetch(`${FORMANT_API_URL}/v1/admin/capture-sessions`, {
|
|
20030
|
+
method: "POST",
|
|
20031
|
+
body: JSON.stringify({
|
|
20032
|
+
deviceId: this.id,
|
|
20033
|
+
streamName,
|
|
20034
|
+
tags: {}
|
|
20035
|
+
}),
|
|
20036
|
+
headers: {
|
|
20037
|
+
"Content-Type": "application/json",
|
|
20038
|
+
Authorization: "Bearer " + Authentication.token
|
|
19855
20039
|
}
|
|
19856
|
-
}
|
|
19857
|
-
requestIdToResponseMap.delete(id);
|
|
19858
|
-
console.error({
|
|
19859
|
-
name: "TimeoutError",
|
|
19860
|
-
message: `Request timed out after ${timeout / 1e3} seconds`
|
|
19861
20040
|
});
|
|
19862
|
-
|
|
20041
|
+
const captureSession = await result.json();
|
|
20042
|
+
return new CaptureStream(captureSession);
|
|
19863
20043
|
}
|
|
19864
|
-
|
|
19865
|
-
|
|
19866
|
-
|
|
19867
|
-
|
|
19868
|
-
|
|
19869
|
-
|
|
19870
|
-
|
|
19871
|
-
|
|
19872
|
-
constructor(id, name, organizationId) {
|
|
19873
|
-
__publicField(this, "rtcClient");
|
|
19874
|
-
__publicField(this, "remoteDevicePeerId");
|
|
19875
|
-
__publicField(this, "realtimeListeners", []);
|
|
19876
|
-
__publicField(this, "handleMessage", (peerId, message) => {
|
|
19877
|
-
this.realtimeListeners.forEach((_) => _(peerId, message));
|
|
19878
|
-
});
|
|
19879
|
-
this.id = id;
|
|
19880
|
-
this.name = name;
|
|
19881
|
-
this.organizationId = organizationId;
|
|
20044
|
+
async getTelemetry(streamNameOrStreamNames, start, end, tags) {
|
|
20045
|
+
return await Fleet.getTelemetry(
|
|
20046
|
+
this.id,
|
|
20047
|
+
streamNameOrStreamNames,
|
|
20048
|
+
start,
|
|
20049
|
+
end,
|
|
20050
|
+
tags
|
|
20051
|
+
);
|
|
19882
20052
|
}
|
|
19883
|
-
async
|
|
19884
|
-
|
|
19885
|
-
|
|
20053
|
+
async getTelemetryStreams() {
|
|
20054
|
+
var _a, _b;
|
|
20055
|
+
const config = await this.getConfiguration();
|
|
20056
|
+
const result = await fetch(
|
|
20057
|
+
`${FORMANT_API_URL}/v1/queries/metadata/stream-names`,
|
|
19886
20058
|
{
|
|
19887
20059
|
method: "POST",
|
|
19888
20060
|
body: JSON.stringify({
|
|
@@ -19894,109 +20066,123 @@ class Device {
|
|
|
19894
20066
|
}
|
|
19895
20067
|
}
|
|
19896
20068
|
);
|
|
19897
|
-
const
|
|
19898
|
-
|
|
19899
|
-
|
|
19900
|
-
|
|
19901
|
-
|
|
19902
|
-
|
|
19903
|
-
|
|
19904
|
-
|
|
19905
|
-
Authorization: "Bearer " + Authentication.token
|
|
20069
|
+
const disabledList = [];
|
|
20070
|
+
const onDemandList = [];
|
|
20071
|
+
(_b = (_a = config.telemetry) == null ? void 0 : _a.streams) == null ? void 0 : _b.forEach((_) => {
|
|
20072
|
+
if (_.disabled !== true) {
|
|
20073
|
+
disabledList.push(_.name);
|
|
20074
|
+
}
|
|
20075
|
+
if (_.onDemand === true) {
|
|
20076
|
+
onDemandList.push(_.name);
|
|
19906
20077
|
}
|
|
19907
20078
|
});
|
|
19908
|
-
|
|
19909
|
-
|
|
19910
|
-
|
|
19911
|
-
|
|
19912
|
-
|
|
19913
|
-
|
|
19914
|
-
const
|
|
19915
|
-
|
|
19916
|
-
`${FORMANT_API_URL}/v1/admin/devices/${this.id}/configurations/${version}`,
|
|
20079
|
+
console.log(onDemandList);
|
|
20080
|
+
const data = await result.json();
|
|
20081
|
+
let streamNames = data.items.filter((_) => !disabledList.includes(_)).map((_) => ({ name: _, onDemand: onDemandList.includes(_) }));
|
|
20082
|
+
return streamNames;
|
|
20083
|
+
}
|
|
20084
|
+
async createInterventionRequest(message, interventionType, interventionRequest, tags) {
|
|
20085
|
+
const intervention = await fetch(
|
|
20086
|
+
`${FORMANT_API_URL}/v1/admin/intervention-requests`,
|
|
19917
20087
|
{
|
|
19918
|
-
method: "
|
|
20088
|
+
method: "POST",
|
|
20089
|
+
body: JSON.stringify({
|
|
20090
|
+
message,
|
|
20091
|
+
interventionType,
|
|
20092
|
+
time: new Date().toISOString(),
|
|
20093
|
+
deviceId: this.id,
|
|
20094
|
+
tags,
|
|
20095
|
+
data: interventionRequest
|
|
20096
|
+
}),
|
|
19919
20097
|
headers: {
|
|
19920
20098
|
"Content-Type": "application/json",
|
|
19921
20099
|
Authorization: "Bearer " + Authentication.token
|
|
19922
20100
|
}
|
|
19923
20101
|
}
|
|
19924
20102
|
);
|
|
19925
|
-
const
|
|
19926
|
-
return
|
|
20103
|
+
const interventionJson = await intervention.json();
|
|
20104
|
+
return interventionJson;
|
|
19927
20105
|
}
|
|
19928
|
-
async
|
|
19929
|
-
const
|
|
19930
|
-
|
|
19931
|
-
|
|
19932
|
-
|
|
19933
|
-
|
|
19934
|
-
|
|
19935
|
-
|
|
19936
|
-
|
|
20106
|
+
async addInterventionResponse(interventionId, interventionType, data) {
|
|
20107
|
+
const response = await fetch(
|
|
20108
|
+
`${FORMANT_API_URL}/v1/admin/intervention-responses`,
|
|
20109
|
+
{
|
|
20110
|
+
method: "POST",
|
|
20111
|
+
body: JSON.stringify({
|
|
20112
|
+
interventionId,
|
|
20113
|
+
interventionType,
|
|
20114
|
+
data
|
|
20115
|
+
}),
|
|
20116
|
+
headers: {
|
|
20117
|
+
"Content-Type": "application/json",
|
|
20118
|
+
Authorization: "Bearer " + Authentication.token
|
|
20119
|
+
}
|
|
19937
20120
|
}
|
|
20121
|
+
);
|
|
20122
|
+
const interventionResponse = await response.json();
|
|
20123
|
+
return interventionResponse;
|
|
20124
|
+
}
|
|
20125
|
+
}
|
|
20126
|
+
class PeerDevice {
|
|
20127
|
+
constructor(peerUrl) {
|
|
20128
|
+
__publicField(this, "rtcClient");
|
|
20129
|
+
__publicField(this, "remoteDevicePeerId");
|
|
20130
|
+
__publicField(this, "realtimeListeners", []);
|
|
20131
|
+
__publicField(this, "id");
|
|
20132
|
+
__publicField(this, "handleMessage", (peerId, message) => {
|
|
20133
|
+
this.realtimeListeners.forEach((_) => _(peerId, message));
|
|
19938
20134
|
});
|
|
19939
|
-
|
|
19940
|
-
|
|
20135
|
+
this.peerUrl = peerUrl;
|
|
20136
|
+
}
|
|
20137
|
+
async getLatestTelemetry() {
|
|
20138
|
+
const data = await fetch(`${this.peerUrl}/telemetry`);
|
|
20139
|
+
const telemetry = await data.json();
|
|
20140
|
+
const entries = Object.entries(telemetry);
|
|
20141
|
+
return entries.map(([stream, latestValue]) => {
|
|
20142
|
+
const v = {
|
|
20143
|
+
deviceId: this.id,
|
|
20144
|
+
streamName: stream,
|
|
20145
|
+
streamType: "json",
|
|
20146
|
+
currentValue: latestValue,
|
|
20147
|
+
currentValueTime: latestValue.timestamp,
|
|
20148
|
+
tags: {}
|
|
20149
|
+
};
|
|
20150
|
+
return v;
|
|
20151
|
+
});
|
|
20152
|
+
}
|
|
20153
|
+
async getDeviceId() {
|
|
20154
|
+
let result = await fetch(`${this.peerUrl}/configuration`);
|
|
20155
|
+
const cfg = await result.json();
|
|
20156
|
+
return cfg.agent_config.id;
|
|
20157
|
+
}
|
|
20158
|
+
async getConfiguration() {
|
|
20159
|
+
let result = await fetch(`${this.peerUrl}/configuration`);
|
|
20160
|
+
const cfg = await result.json();
|
|
20161
|
+
return cfg.agent_config.document;
|
|
19941
20162
|
}
|
|
19942
20163
|
getRealtimeStatus() {
|
|
19943
20164
|
if (this.rtcClient && this.remoteDevicePeerId) {
|
|
19944
20165
|
return this.rtcClient.getConnectionStatus(this.remoteDevicePeerId);
|
|
19945
20166
|
} else {
|
|
19946
|
-
throw new Error(`Realtime connection hasn't been started
|
|
20167
|
+
throw new Error(`Realtime connection hasn't been started`);
|
|
19947
20168
|
}
|
|
19948
20169
|
}
|
|
19949
20170
|
getRealtimePing() {
|
|
19950
20171
|
if (this.rtcClient && this.remoteDevicePeerId) {
|
|
19951
20172
|
return this.rtcClient.getPing(this.remoteDevicePeerId);
|
|
19952
20173
|
} else {
|
|
19953
|
-
throw new Error(`Realtime connection hasn't been started
|
|
20174
|
+
throw new Error(`Realtime connection hasn't been started`);
|
|
19954
20175
|
}
|
|
19955
20176
|
}
|
|
19956
20177
|
async startRealtimeConnection(sessionType) {
|
|
19957
20178
|
if (!this.rtcClient) {
|
|
19958
|
-
|
|
19959
|
-
|
|
19960
|
-
|
|
19961
|
-
|
|
19962
|
-
|
|
19963
|
-
|
|
19964
|
-
|
|
19965
|
-
Authentication.token,
|
|
19966
|
-
"Realtime when user isn't authorized"
|
|
19967
|
-
),
|
|
19968
|
-
receive: this.handleMessage
|
|
19969
|
-
});
|
|
19970
|
-
} else {
|
|
19971
|
-
rtcClient = new dist.exports.RtcClient({
|
|
19972
|
-
signalingClient: new dist.exports.SignalingPromiseClient(
|
|
19973
|
-
FORMANT_API_URL,
|
|
19974
|
-
null,
|
|
19975
|
-
null
|
|
19976
|
-
),
|
|
19977
|
-
getToken: async () => {
|
|
19978
|
-
return defined(
|
|
19979
|
-
Authentication.token,
|
|
19980
|
-
"Realtime when user isn't authorized"
|
|
19981
|
-
);
|
|
19982
|
-
},
|
|
19983
|
-
receive: this.handleMessage,
|
|
19984
|
-
sessionType
|
|
19985
|
-
});
|
|
19986
|
-
}
|
|
19987
|
-
if (rtcClient.isReady) {
|
|
19988
|
-
while (!rtcClient.isReady()) {
|
|
19989
|
-
await delay(100);
|
|
19990
|
-
}
|
|
19991
|
-
}
|
|
19992
|
-
const peers = await rtcClient.getPeers();
|
|
19993
|
-
const devicePeer = peers.find((_) => _.deviceId === this.id);
|
|
19994
|
-
if (!devicePeer) {
|
|
19995
|
-
throw new Error("Cannot find peer, is the robot offline?");
|
|
19996
|
-
}
|
|
19997
|
-
this.remoteDevicePeerId = devicePeer.id;
|
|
19998
|
-
await rtcClient.connect(this.remoteDevicePeerId);
|
|
19999
|
-
while (rtcClient.getConnectionStatus(this.remoteDevicePeerId) !== "connected") {
|
|
20179
|
+
const rtcClient = new dist.exports.RtcClient({
|
|
20180
|
+
lanOnlyMode: true,
|
|
20181
|
+
receive: this.handleMessage,
|
|
20182
|
+
sessionType
|
|
20183
|
+
});
|
|
20184
|
+
await rtcClient.connectLan(this.peerUrl);
|
|
20185
|
+
while (rtcClient.getConnectionStatus(this.peerUrl) !== "connected") {
|
|
20000
20186
|
await delay(100);
|
|
20001
20187
|
}
|
|
20002
20188
|
this.rtcClient = rtcClient;
|
|
@@ -20006,16 +20192,6 @@ class Device {
|
|
|
20006
20192
|
);
|
|
20007
20193
|
}
|
|
20008
20194
|
}
|
|
20009
|
-
async sendRealtimeMessage(message, config = {
|
|
20010
|
-
channelLabel: "stream.reliable"
|
|
20011
|
-
}) {
|
|
20012
|
-
const client = defined(
|
|
20013
|
-
this.rtcClient,
|
|
20014
|
-
"Realtime connection has not been started"
|
|
20015
|
-
);
|
|
20016
|
-
const devicePeer = await this.getRemotePeer();
|
|
20017
|
-
client.send(defined(devicePeer).id, message, config);
|
|
20018
|
-
}
|
|
20019
20195
|
addRealtimeListener(listener) {
|
|
20020
20196
|
this.realtimeListeners.push(listener);
|
|
20021
20197
|
}
|
|
@@ -20024,54 +20200,27 @@ class Device {
|
|
|
20024
20200
|
if (i === -1) {
|
|
20025
20201
|
throw new Error("Could not find realtime listener to remove");
|
|
20026
20202
|
}
|
|
20027
|
-
this.realtimeListeners.splice(i, 1);
|
|
20028
|
-
}
|
|
20029
|
-
async getRealtimeAudioStreams() {
|
|
20030
|
-
var _a, _b, _c, _d, _e, _f;
|
|
20031
|
-
const document2 = await this.getConfiguration();
|
|
20032
|
-
const streams = [];
|
|
20033
|
-
for (const _ of (_b = (_a = document2.teleop) == null ? void 0 : _a.hardwareStreams) != null ? _b : []) {
|
|
20034
|
-
if (_.rtcStreamType === "audio-chunk") {
|
|
20035
|
-
streams.push({
|
|
20036
|
-
name: _.name
|
|
20037
|
-
});
|
|
20038
|
-
}
|
|
20039
|
-
}
|
|
20040
|
-
for (const _ of (_d = (_c = document2.teleop) == null ? void 0 : _c.rosStreams) != null ? _d : []) {
|
|
20041
|
-
if (_.topicType == "audio_common_msgs/AudioData") {
|
|
20042
|
-
streams.push({
|
|
20043
|
-
name: _.topicName
|
|
20044
|
-
});
|
|
20045
|
-
}
|
|
20046
|
-
}
|
|
20047
|
-
for (const _ of (_f = (_e = document2.teleop) == null ? void 0 : _e.customStreams) != null ? _f : []) {
|
|
20048
|
-
if (_.rtcStreamType === "audio-chunk") {
|
|
20049
|
-
streams.push({
|
|
20050
|
-
name: _.name
|
|
20051
|
-
});
|
|
20052
|
-
}
|
|
20053
|
-
}
|
|
20054
|
-
return streams;
|
|
20203
|
+
this.realtimeListeners.splice(i, 1);
|
|
20055
20204
|
}
|
|
20056
20205
|
async getRealtimeVideoStreams() {
|
|
20057
|
-
var _a, _b, _c
|
|
20206
|
+
var _a, _b, _c;
|
|
20058
20207
|
const document2 = await this.getConfiguration();
|
|
20059
20208
|
const streams = [];
|
|
20060
|
-
for (const _ of (
|
|
20209
|
+
for (const _ of (_a = document2.teleop.hardwareStreams) != null ? _a : []) {
|
|
20061
20210
|
if (_.rtcStreamType === "h264-video-frame") {
|
|
20062
20211
|
streams.push({
|
|
20063
20212
|
name: _.name
|
|
20064
20213
|
});
|
|
20065
20214
|
}
|
|
20066
20215
|
}
|
|
20067
|
-
for (const _ of (
|
|
20216
|
+
for (const _ of (_b = document2.teleop.rosStreams) != null ? _b : []) {
|
|
20068
20217
|
if (_.topicType == "formant/H264VideoFrame") {
|
|
20069
20218
|
streams.push({
|
|
20070
20219
|
name: _.topicName
|
|
20071
20220
|
});
|
|
20072
20221
|
}
|
|
20073
20222
|
}
|
|
20074
|
-
for (const _ of (
|
|
20223
|
+
for (const _ of (_c = document2.teleop.customStreams) != null ? _c : []) {
|
|
20075
20224
|
if (_.rtcStreamType === "h264-video-frame") {
|
|
20076
20225
|
streams.push({
|
|
20077
20226
|
name: _.name
|
|
@@ -20161,17 +20310,6 @@ class Device {
|
|
|
20161
20310
|
pipeline: "telemetry"
|
|
20162
20311
|
});
|
|
20163
20312
|
}
|
|
20164
|
-
async changeStreamAudioType(streamName, newFormat) {
|
|
20165
|
-
const client = defined(
|
|
20166
|
-
this.rtcClient,
|
|
20167
|
-
"Realtime connection has not been started"
|
|
20168
|
-
);
|
|
20169
|
-
const devicePeer = await this.getRemotePeer();
|
|
20170
|
-
client.controlRemoteStream(defined(devicePeer).id, {
|
|
20171
|
-
streamName,
|
|
20172
|
-
setAudioFormat: newFormat
|
|
20173
|
-
});
|
|
20174
|
-
}
|
|
20175
20313
|
async disableRealtimeTelemetryPriorityIngestion(streamName) {
|
|
20176
20314
|
const client = defined(
|
|
20177
20315
|
this.rtcClient,
|
|
@@ -20185,15 +20323,13 @@ class Device {
|
|
|
20185
20323
|
});
|
|
20186
20324
|
}
|
|
20187
20325
|
async getRemotePeer() {
|
|
20188
|
-
|
|
20189
|
-
this.
|
|
20190
|
-
|
|
20191
|
-
|
|
20192
|
-
|
|
20193
|
-
|
|
20194
|
-
|
|
20195
|
-
"Could not find remote peer for device " + this.id
|
|
20196
|
-
);
|
|
20326
|
+
return {
|
|
20327
|
+
id: this.peerUrl,
|
|
20328
|
+
organizationId: "",
|
|
20329
|
+
deviceId: this.id,
|
|
20330
|
+
capabilities: [],
|
|
20331
|
+
capabilitySet: {}
|
|
20332
|
+
};
|
|
20197
20333
|
}
|
|
20198
20334
|
async stopRealtimeConnection() {
|
|
20199
20335
|
if (this.rtcClient) {
|
|
@@ -20202,18 +20338,82 @@ class Device {
|
|
|
20202
20338
|
throw new Error(`Realtime connection hasn't been started for ${this.id}`);
|
|
20203
20339
|
}
|
|
20204
20340
|
}
|
|
20205
|
-
async
|
|
20206
|
-
|
|
20207
|
-
|
|
20208
|
-
|
|
20209
|
-
|
|
20210
|
-
|
|
20341
|
+
async createCustomDataChannel(channelName, rtcConfig) {
|
|
20342
|
+
const client = defined(
|
|
20343
|
+
this.rtcClient,
|
|
20344
|
+
"Realtime connection has not been started"
|
|
20345
|
+
);
|
|
20346
|
+
const devicePeer = await this.getRemotePeer();
|
|
20347
|
+
const p = await new Promise((resolve) => {
|
|
20348
|
+
client.createCustomDataChannel(
|
|
20349
|
+
defined(devicePeer).id,
|
|
20350
|
+
channelName,
|
|
20351
|
+
{
|
|
20352
|
+
ordered: true,
|
|
20353
|
+
...rtcConfig
|
|
20354
|
+
},
|
|
20355
|
+
false,
|
|
20356
|
+
(_peerId, channel) => {
|
|
20357
|
+
const dataChannel = new DataChannel(channel);
|
|
20358
|
+
resolve(dataChannel);
|
|
20359
|
+
}
|
|
20360
|
+
);
|
|
20361
|
+
});
|
|
20362
|
+
await p.waitTilReady();
|
|
20363
|
+
return p;
|
|
20364
|
+
}
|
|
20365
|
+
createCustomRequestDataChannel(channelName, timeout = 3e3) {
|
|
20366
|
+
return new TextRequestDataChannel(this, channelName, timeout);
|
|
20367
|
+
}
|
|
20368
|
+
createCustomBinaryRequestDataChannel(channelName, timeout = 3e3) {
|
|
20369
|
+
return new BinaryRequestDataChannel(this, channelName, timeout);
|
|
20370
|
+
}
|
|
20371
|
+
}
|
|
20372
|
+
const _Fleet$1 = class {
|
|
20373
|
+
static async setDefaultDevice(deviceId) {
|
|
20374
|
+
_Fleet$1.defaultDeviceId = deviceId;
|
|
20375
|
+
}
|
|
20376
|
+
static async getCurrentDevice() {
|
|
20377
|
+
if (!Authentication.token) {
|
|
20378
|
+
throw new Error("Not authenticated");
|
|
20211
20379
|
}
|
|
20212
|
-
|
|
20380
|
+
if (!_Fleet$1.defaultDeviceId) {
|
|
20381
|
+
throw new Error("No known default device");
|
|
20382
|
+
}
|
|
20383
|
+
const data = await fetch(
|
|
20384
|
+
`${FORMANT_API_URL}/v1/admin/device-details/query`,
|
|
20385
|
+
{
|
|
20386
|
+
method: "POST",
|
|
20387
|
+
headers: {
|
|
20388
|
+
"Content-Type": "application/json",
|
|
20389
|
+
Authorization: "Bearer " + Authentication.token
|
|
20390
|
+
}
|
|
20391
|
+
}
|
|
20392
|
+
);
|
|
20393
|
+
const devices = await data.json();
|
|
20394
|
+
const device = devices.items.find(
|
|
20395
|
+
(_) => _.id === _Fleet$1.defaultDeviceId
|
|
20396
|
+
);
|
|
20397
|
+
const name = device.name;
|
|
20398
|
+
const context = new Device(
|
|
20399
|
+
_Fleet$1.defaultDeviceId,
|
|
20400
|
+
name,
|
|
20401
|
+
defined(Authentication.currentOrganization)
|
|
20402
|
+
);
|
|
20403
|
+
_Fleet$1.knownContext.push(new WeakRef(context));
|
|
20404
|
+
return context;
|
|
20213
20405
|
}
|
|
20214
|
-
async
|
|
20215
|
-
const
|
|
20216
|
-
|
|
20406
|
+
static async getPeerDevice(url) {
|
|
20407
|
+
const peer = new PeerDevice(url);
|
|
20408
|
+
peer.id = await peer.getDeviceId();
|
|
20409
|
+
return peer;
|
|
20410
|
+
}
|
|
20411
|
+
static async getDevice(deviceId) {
|
|
20412
|
+
if (!Authentication.token) {
|
|
20413
|
+
throw new Error("Not authenticated");
|
|
20414
|
+
}
|
|
20415
|
+
const data = await fetch(
|
|
20416
|
+
`${FORMANT_API_URL}/v1/admin/devices/${deviceId}`,
|
|
20217
20417
|
{
|
|
20218
20418
|
method: "GET",
|
|
20219
20419
|
headers: {
|
|
@@ -20222,440 +20422,868 @@ class Device {
|
|
|
20222
20422
|
}
|
|
20223
20423
|
}
|
|
20224
20424
|
);
|
|
20225
|
-
const
|
|
20226
|
-
|
|
20227
|
-
|
|
20228
|
-
|
|
20229
|
-
|
|
20230
|
-
description: i.description,
|
|
20231
|
-
parameterEnabled: i.parameterEnabled,
|
|
20232
|
-
parameterValue: i.parameterValue,
|
|
20233
|
-
parameterMeta: i.parameterMeta,
|
|
20234
|
-
enabled: i.enabled
|
|
20235
|
-
}));
|
|
20425
|
+
const device = await data.json();
|
|
20426
|
+
const name = device.name;
|
|
20427
|
+
const context = new Device(deviceId, name, device.organizationId);
|
|
20428
|
+
_Fleet$1.knownContext.push(new WeakRef(context));
|
|
20429
|
+
return context;
|
|
20236
20430
|
}
|
|
20237
|
-
async
|
|
20238
|
-
|
|
20239
|
-
|
|
20240
|
-
const command = commands.find((_) => _.name === name);
|
|
20241
|
-
if (!command) {
|
|
20242
|
-
throw new Error(`Could not find command with name "${name}"`);
|
|
20431
|
+
static async getDevices() {
|
|
20432
|
+
if (!Authentication.token) {
|
|
20433
|
+
throw new Error("Not authenticated");
|
|
20243
20434
|
}
|
|
20244
|
-
|
|
20245
|
-
|
|
20246
|
-
|
|
20247
|
-
|
|
20248
|
-
|
|
20249
|
-
|
|
20250
|
-
"
|
|
20435
|
+
const data = await fetch(
|
|
20436
|
+
`${FORMANT_API_URL}/v1/admin/device-details/query`,
|
|
20437
|
+
{
|
|
20438
|
+
method: "POST",
|
|
20439
|
+
body: JSON.stringify({ enabled: true, type: "default" }),
|
|
20440
|
+
headers: {
|
|
20441
|
+
"Content-Type": "application/json",
|
|
20442
|
+
Authorization: "Bearer " + Authentication.token
|
|
20443
|
+
}
|
|
20444
|
+
}
|
|
20445
|
+
);
|
|
20446
|
+
const devices = await data.json();
|
|
20447
|
+
devices.items;
|
|
20448
|
+
return devices.items.map(
|
|
20449
|
+
(_) => new Device(_.id, _.name, _.organizationId)
|
|
20450
|
+
);
|
|
20451
|
+
}
|
|
20452
|
+
static async queryDevices(query) {
|
|
20453
|
+
if (!Authentication.token) {
|
|
20454
|
+
throw new Error("Not authenticated");
|
|
20455
|
+
}
|
|
20456
|
+
const data = await fetch(`${FORMANT_API_URL}/v1/admin/devices/query`, {
|
|
20457
|
+
method: "POST",
|
|
20458
|
+
body: JSON.stringify(query),
|
|
20459
|
+
headers: {
|
|
20460
|
+
"Content-Type": "application/json",
|
|
20461
|
+
Authorization: "Bearer " + Authentication.token
|
|
20462
|
+
}
|
|
20463
|
+
});
|
|
20464
|
+
const devices = await data.json();
|
|
20465
|
+
return devices.items.map(
|
|
20466
|
+
(_) => new Device(_.id, _.name, _.organizationId)
|
|
20467
|
+
);
|
|
20468
|
+
}
|
|
20469
|
+
static async getOnlineDevices() {
|
|
20470
|
+
if (!Authentication.token) {
|
|
20471
|
+
throw new Error("Not authenticated");
|
|
20472
|
+
}
|
|
20473
|
+
const data = await fetch(`${FORMANT_API_URL}/v1/queries/online-devices`, {
|
|
20474
|
+
method: "GET",
|
|
20475
|
+
headers: {
|
|
20476
|
+
"Content-Type": "application/json",
|
|
20477
|
+
Authorization: "Bearer " + Authentication.token
|
|
20478
|
+
}
|
|
20479
|
+
});
|
|
20480
|
+
const devices = await data.json();
|
|
20481
|
+
const onlineIds = devices.items;
|
|
20482
|
+
const allDevices = await _Fleet$1.getDevices();
|
|
20483
|
+
return allDevices.filter((_) => onlineIds.includes(_.id));
|
|
20484
|
+
}
|
|
20485
|
+
static async getPeers() {
|
|
20486
|
+
if (!Authentication.token) {
|
|
20487
|
+
throw new Error("Not authenticated");
|
|
20488
|
+
}
|
|
20489
|
+
const rtcClient = new dist.exports.RtcClient({
|
|
20490
|
+
signalingClient: new dist.exports.SignalingPromiseClient(FORMANT_API_URL, null, null),
|
|
20491
|
+
getToken: async () => {
|
|
20492
|
+
return defined(
|
|
20493
|
+
Authentication.token,
|
|
20494
|
+
"Realtime when user isn't authorized"
|
|
20495
|
+
);
|
|
20496
|
+
},
|
|
20497
|
+
receive: () => {
|
|
20498
|
+
}
|
|
20499
|
+
});
|
|
20500
|
+
return await rtcClient.getPeers();
|
|
20501
|
+
}
|
|
20502
|
+
static async getRealtimeSessions() {
|
|
20503
|
+
if (!Authentication.token) {
|
|
20504
|
+
throw new Error("Not authenticated");
|
|
20505
|
+
}
|
|
20506
|
+
const rtcClient = new dist.exports.RtcClient({
|
|
20507
|
+
signalingClient: new dist.exports.SignalingPromiseClient(FORMANT_API_URL, null, null),
|
|
20508
|
+
getToken: async () => {
|
|
20509
|
+
return defined(
|
|
20510
|
+
Authentication.token,
|
|
20511
|
+
"Realtime when user isn't authorized"
|
|
20251
20512
|
);
|
|
20513
|
+
},
|
|
20514
|
+
receive: () => {
|
|
20515
|
+
}
|
|
20516
|
+
});
|
|
20517
|
+
return await rtcClient.getSessions();
|
|
20518
|
+
}
|
|
20519
|
+
static async getRealtimeDevices() {
|
|
20520
|
+
if (!Authentication.token) {
|
|
20521
|
+
throw new Error("Not authenticated");
|
|
20522
|
+
}
|
|
20523
|
+
const data = await fetch(`${FORMANT_API_URL}/v1/signaling/peers`, {
|
|
20524
|
+
method: "GET",
|
|
20525
|
+
headers: {
|
|
20526
|
+
"Content-Type": "application/json",
|
|
20527
|
+
Authorization: "Bearer " + Authentication.token
|
|
20528
|
+
}
|
|
20529
|
+
});
|
|
20530
|
+
const devices = await data.json();
|
|
20531
|
+
const onlineIds = devices.items.map(
|
|
20532
|
+
(_) => _.deviceId
|
|
20533
|
+
);
|
|
20534
|
+
const allDevices = await _Fleet$1.getDevices();
|
|
20535
|
+
return allDevices.filter((_) => onlineIds.includes(_.id));
|
|
20536
|
+
}
|
|
20537
|
+
static async getLatestTelemetry(deviceIdOrDeviceIds) {
|
|
20538
|
+
let deviceIds = deviceIdOrDeviceIds;
|
|
20539
|
+
if (deviceIdOrDeviceIds && !Array.isArray(deviceIdOrDeviceIds)) {
|
|
20540
|
+
deviceIdOrDeviceIds = [deviceIdOrDeviceIds];
|
|
20541
|
+
}
|
|
20542
|
+
const data = await fetch(
|
|
20543
|
+
`${FORMANT_API_URL}/v1/queries/stream-current-value`,
|
|
20544
|
+
{
|
|
20545
|
+
method: "POST",
|
|
20546
|
+
body: JSON.stringify({
|
|
20547
|
+
deviceIds
|
|
20548
|
+
}),
|
|
20549
|
+
headers: {
|
|
20550
|
+
"Content-Type": "application/json",
|
|
20551
|
+
Authorization: "Bearer " + Authentication.token
|
|
20552
|
+
}
|
|
20252
20553
|
}
|
|
20253
|
-
|
|
20254
|
-
|
|
20554
|
+
);
|
|
20555
|
+
const telemetry = await data.json();
|
|
20556
|
+
return telemetry.items;
|
|
20557
|
+
}
|
|
20558
|
+
static async getTelemetry(deviceIdOrDeviceIds, streamNameOrStreamNames, start, end, tags) {
|
|
20559
|
+
let deviceIds = deviceIdOrDeviceIds;
|
|
20560
|
+
if (!Array.isArray(deviceIdOrDeviceIds)) {
|
|
20561
|
+
deviceIds = [deviceIdOrDeviceIds];
|
|
20255
20562
|
}
|
|
20256
|
-
let
|
|
20257
|
-
|
|
20258
|
-
|
|
20259
|
-
|
|
20260
|
-
|
|
20261
|
-
|
|
20563
|
+
let streamNames = streamNameOrStreamNames;
|
|
20564
|
+
if (!Array.isArray(streamNameOrStreamNames)) {
|
|
20565
|
+
streamNames = [streamNameOrStreamNames];
|
|
20566
|
+
}
|
|
20567
|
+
const data = await fetch(`${FORMANT_API_URL}/v1/queries/queries`, {
|
|
20568
|
+
method: "POST",
|
|
20569
|
+
body: JSON.stringify({
|
|
20570
|
+
deviceIds,
|
|
20571
|
+
end: end.toISOString(),
|
|
20572
|
+
names: streamNames,
|
|
20573
|
+
start: start.toISOString(),
|
|
20574
|
+
tags
|
|
20575
|
+
}),
|
|
20576
|
+
headers: {
|
|
20577
|
+
"Content-Type": "application/json",
|
|
20578
|
+
Authorization: "Bearer " + Authentication.token
|
|
20262
20579
|
}
|
|
20263
|
-
};
|
|
20264
|
-
await
|
|
20580
|
+
});
|
|
20581
|
+
const telemetry = await data.json();
|
|
20582
|
+
return telemetry.items;
|
|
20583
|
+
}
|
|
20584
|
+
static async getFileUrl(uuid) {
|
|
20585
|
+
const data = await fetch(`${FORMANT_API_URL}/v1/admin/files/query`, {
|
|
20265
20586
|
method: "POST",
|
|
20266
20587
|
body: JSON.stringify({
|
|
20267
|
-
|
|
20268
|
-
organizationId: this.organizationId,
|
|
20269
|
-
deviceId: this.id,
|
|
20270
|
-
command: command.command,
|
|
20271
|
-
parameter,
|
|
20272
|
-
userId: (_a = Authentication.currentUser) == null ? void 0 : _a.id
|
|
20588
|
+
fileIds: [uuid]
|
|
20273
20589
|
}),
|
|
20274
20590
|
headers: {
|
|
20275
20591
|
"Content-Type": "application/json",
|
|
20276
20592
|
Authorization: "Bearer " + Authentication.token
|
|
20277
20593
|
}
|
|
20278
20594
|
});
|
|
20595
|
+
const result = await data.json();
|
|
20596
|
+
if (result.fileUrls.length === 0) {
|
|
20597
|
+
throw new Error("File not found");
|
|
20598
|
+
}
|
|
20599
|
+
return result.fileUrls[0];
|
|
20279
20600
|
}
|
|
20280
|
-
async
|
|
20281
|
-
if (
|
|
20282
|
-
throw new Error(
|
|
20283
|
-
"createCustomDataChannel is not supported in rtcClientVersion 1"
|
|
20284
|
-
);
|
|
20601
|
+
static async queryTelemetry(query) {
|
|
20602
|
+
if (!Authentication.token) {
|
|
20603
|
+
throw new Error("Not authenticated");
|
|
20285
20604
|
}
|
|
20286
|
-
const
|
|
20287
|
-
|
|
20288
|
-
|
|
20289
|
-
|
|
20290
|
-
|
|
20291
|
-
|
|
20292
|
-
|
|
20293
|
-
defined(devicePeer).id,
|
|
20294
|
-
channelName,
|
|
20295
|
-
{
|
|
20296
|
-
ordered: true,
|
|
20297
|
-
...rtcConfig
|
|
20298
|
-
},
|
|
20299
|
-
false,
|
|
20300
|
-
(_peerId, channel) => {
|
|
20301
|
-
const dataChannel = new DataChannel(channel);
|
|
20302
|
-
resolve(dataChannel);
|
|
20303
|
-
}
|
|
20304
|
-
);
|
|
20605
|
+
const data = await fetch(`${FORMANT_API_URL}/v1/queries/queries`, {
|
|
20606
|
+
method: "POST",
|
|
20607
|
+
body: JSON.stringify(query),
|
|
20608
|
+
headers: {
|
|
20609
|
+
"Content-Type": "application/json",
|
|
20610
|
+
Authorization: "Bearer " + Authentication.token
|
|
20611
|
+
}
|
|
20305
20612
|
});
|
|
20306
|
-
await
|
|
20307
|
-
return p;
|
|
20308
|
-
}
|
|
20309
|
-
createCustomRequestDataChannel(channelName, timeout = 3e3) {
|
|
20310
|
-
return new TextRequestDataChannel(this, channelName, timeout);
|
|
20613
|
+
return (await data.json()).items;
|
|
20311
20614
|
}
|
|
20312
|
-
|
|
20313
|
-
|
|
20615
|
+
static async aggregateTelemetry(query) {
|
|
20616
|
+
if (!Authentication.token) {
|
|
20617
|
+
throw new Error("Not authenticated");
|
|
20618
|
+
}
|
|
20619
|
+
const data = await fetch(`${FORMANT_API_URL}/v1/queries/queries`, {
|
|
20620
|
+
method: "POST",
|
|
20621
|
+
body: JSON.stringify(query),
|
|
20622
|
+
headers: {
|
|
20623
|
+
"Content-Type": "application/json",
|
|
20624
|
+
Authorization: "Bearer " + Authentication.token
|
|
20625
|
+
}
|
|
20626
|
+
});
|
|
20627
|
+
return (await data.json()).aggregates;
|
|
20314
20628
|
}
|
|
20315
|
-
async
|
|
20316
|
-
|
|
20629
|
+
static async queryEvents(query) {
|
|
20630
|
+
if (!Authentication.token) {
|
|
20631
|
+
throw new Error("Not authenticated");
|
|
20632
|
+
}
|
|
20633
|
+
const data = await fetch(`${FORMANT_API_URL}/v1/admin/events/query`, {
|
|
20317
20634
|
method: "POST",
|
|
20318
|
-
body: JSON.stringify(
|
|
20319
|
-
deviceId: this.id,
|
|
20320
|
-
streamName,
|
|
20321
|
-
tags: {}
|
|
20322
|
-
}),
|
|
20635
|
+
body: JSON.stringify(query),
|
|
20323
20636
|
headers: {
|
|
20324
20637
|
"Content-Type": "application/json",
|
|
20325
20638
|
Authorization: "Bearer " + Authentication.token
|
|
20326
20639
|
}
|
|
20327
20640
|
});
|
|
20328
|
-
|
|
20329
|
-
return new CaptureStream(captureSession);
|
|
20641
|
+
return (await data.json()).items;
|
|
20330
20642
|
}
|
|
20331
|
-
async
|
|
20332
|
-
|
|
20333
|
-
|
|
20334
|
-
|
|
20335
|
-
|
|
20336
|
-
|
|
20337
|
-
|
|
20643
|
+
static async getEvent(uuid) {
|
|
20644
|
+
if (!Authentication.token) {
|
|
20645
|
+
throw new Error("Not authenticated");
|
|
20646
|
+
}
|
|
20647
|
+
const data = await fetch(
|
|
20648
|
+
`${FORMANT_API_URL}/v1/admin/events/query/id=${uuid}`,
|
|
20649
|
+
{
|
|
20650
|
+
method: "GET",
|
|
20651
|
+
headers: {
|
|
20652
|
+
"Content-Type": "application/json",
|
|
20653
|
+
Authorization: "Bearer " + Authentication.token
|
|
20654
|
+
}
|
|
20655
|
+
}
|
|
20338
20656
|
);
|
|
20657
|
+
return (await data.json()).items;
|
|
20339
20658
|
}
|
|
20340
|
-
async
|
|
20341
|
-
|
|
20342
|
-
|
|
20343
|
-
|
|
20344
|
-
|
|
20659
|
+
static async getInterventions() {
|
|
20660
|
+
if (!Authentication.token) {
|
|
20661
|
+
throw new Error("Not authenticated");
|
|
20662
|
+
}
|
|
20663
|
+
const interventions = await fetch(
|
|
20664
|
+
`${FORMANT_API_URL}/v1/admin/intervention-requests`,
|
|
20345
20665
|
{
|
|
20346
|
-
method: "
|
|
20347
|
-
body: JSON.stringify({
|
|
20348
|
-
deviceIds: [this.id]
|
|
20349
|
-
}),
|
|
20666
|
+
method: "GET",
|
|
20350
20667
|
headers: {
|
|
20351
20668
|
"Content-Type": "application/json",
|
|
20352
20669
|
Authorization: "Bearer " + Authentication.token
|
|
20353
20670
|
}
|
|
20354
20671
|
}
|
|
20355
20672
|
);
|
|
20356
|
-
|
|
20357
|
-
|
|
20358
|
-
|
|
20359
|
-
|
|
20360
|
-
|
|
20673
|
+
return (await interventions.json()).items;
|
|
20674
|
+
}
|
|
20675
|
+
static async getCurrentGroup() {
|
|
20676
|
+
if (!Authentication.token) {
|
|
20677
|
+
throw new Error("Not authenticated");
|
|
20678
|
+
}
|
|
20679
|
+
let urlParams2 = new URLSearchParams("");
|
|
20680
|
+
if (typeof window !== "undefined") {
|
|
20681
|
+
urlParams2 = new URLSearchParams(window.location.search);
|
|
20682
|
+
}
|
|
20683
|
+
const groupId = urlParams2.get("group");
|
|
20684
|
+
if (groupId === null || groupId.trim() === "") {
|
|
20685
|
+
return void 0;
|
|
20686
|
+
}
|
|
20687
|
+
const response = await fetch(
|
|
20688
|
+
`${FORMANT_API_URL}/v1/admin/groups/` + groupId,
|
|
20689
|
+
{
|
|
20690
|
+
headers: {
|
|
20691
|
+
"Content-Type": "application/json",
|
|
20692
|
+
Authorization: "Bearer " + Authentication.token
|
|
20693
|
+
}
|
|
20361
20694
|
}
|
|
20362
|
-
|
|
20363
|
-
|
|
20695
|
+
);
|
|
20696
|
+
const { tagKey, tagValue } = await response.json();
|
|
20697
|
+
const devices = await this.queryDevices({
|
|
20698
|
+
tags: { [tagKey]: [tagValue] },
|
|
20699
|
+
enabled: true,
|
|
20700
|
+
type: "default"
|
|
20701
|
+
});
|
|
20702
|
+
return devices;
|
|
20703
|
+
}
|
|
20704
|
+
};
|
|
20705
|
+
let Fleet$1 = _Fleet$1;
|
|
20706
|
+
__publicField(Fleet$1, "defaultDeviceId");
|
|
20707
|
+
__publicField(Fleet$1, "knownContext", []);
|
|
20708
|
+
function toInteger(dirtyNumber) {
|
|
20709
|
+
if (dirtyNumber === null || dirtyNumber === true || dirtyNumber === false) {
|
|
20710
|
+
return NaN;
|
|
20711
|
+
}
|
|
20712
|
+
var number = Number(dirtyNumber);
|
|
20713
|
+
if (isNaN(number)) {
|
|
20714
|
+
return number;
|
|
20715
|
+
}
|
|
20716
|
+
return number < 0 ? Math.ceil(number) : Math.floor(number);
|
|
20717
|
+
}
|
|
20718
|
+
function requiredArgs(required, args) {
|
|
20719
|
+
if (args.length < required) {
|
|
20720
|
+
throw new TypeError(required + " argument" + (required > 1 ? "s" : "") + " required, but only " + args.length + " present");
|
|
20721
|
+
}
|
|
20722
|
+
}
|
|
20723
|
+
function _typeof(obj) {
|
|
20724
|
+
"@babel/helpers - typeof";
|
|
20725
|
+
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
|
|
20726
|
+
_typeof = function _typeof2(obj2) {
|
|
20727
|
+
return typeof obj2;
|
|
20728
|
+
};
|
|
20729
|
+
} else {
|
|
20730
|
+
_typeof = function _typeof2(obj2) {
|
|
20731
|
+
return obj2 && typeof Symbol === "function" && obj2.constructor === Symbol && obj2 !== Symbol.prototype ? "symbol" : typeof obj2;
|
|
20732
|
+
};
|
|
20733
|
+
}
|
|
20734
|
+
return _typeof(obj);
|
|
20735
|
+
}
|
|
20736
|
+
function toDate(argument) {
|
|
20737
|
+
requiredArgs(1, arguments);
|
|
20738
|
+
var argStr = Object.prototype.toString.call(argument);
|
|
20739
|
+
if (argument instanceof Date || _typeof(argument) === "object" && argStr === "[object Date]") {
|
|
20740
|
+
return new Date(argument.getTime());
|
|
20741
|
+
} else if (typeof argument === "number" || argStr === "[object Number]") {
|
|
20742
|
+
return new Date(argument);
|
|
20743
|
+
} else {
|
|
20744
|
+
if ((typeof argument === "string" || argStr === "[object String]") && typeof console !== "undefined") {
|
|
20745
|
+
console.warn("Starting with v2.0.0-beta.1 date-fns doesn't accept strings as date arguments. Please use `parseISO` to parse strings. See: https://github.com/date-fns/date-fns/blob/master/docs/upgradeGuide.md#string-arguments");
|
|
20746
|
+
console.warn(new Error().stack);
|
|
20747
|
+
}
|
|
20748
|
+
return new Date(NaN);
|
|
20749
|
+
}
|
|
20750
|
+
}
|
|
20751
|
+
function addMilliseconds(dirtyDate, dirtyAmount) {
|
|
20752
|
+
requiredArgs(2, arguments);
|
|
20753
|
+
var timestamp = toDate(dirtyDate).getTime();
|
|
20754
|
+
var amount = toInteger(dirtyAmount);
|
|
20755
|
+
return new Date(timestamp + amount);
|
|
20756
|
+
}
|
|
20757
|
+
var MILLISECONDS_IN_MINUTE = 6e4;
|
|
20758
|
+
function addMinutes(dirtyDate, dirtyAmount) {
|
|
20759
|
+
requiredArgs(2, arguments);
|
|
20760
|
+
var amount = toInteger(dirtyAmount);
|
|
20761
|
+
return addMilliseconds(dirtyDate, amount * MILLISECONDS_IN_MINUTE);
|
|
20762
|
+
}
|
|
20763
|
+
function addSeconds(dirtyDate, dirtyAmount) {
|
|
20764
|
+
requiredArgs(2, arguments);
|
|
20765
|
+
var amount = toInteger(dirtyAmount);
|
|
20766
|
+
return addMilliseconds(dirtyDate, amount * 1e3);
|
|
20767
|
+
}
|
|
20768
|
+
var roundingMap = {
|
|
20769
|
+
ceil: Math.ceil,
|
|
20770
|
+
round: Math.round,
|
|
20771
|
+
floor: Math.floor,
|
|
20772
|
+
trunc: function trunc(value) {
|
|
20773
|
+
return value < 0 ? Math.ceil(value) : Math.floor(value);
|
|
20774
|
+
}
|
|
20775
|
+
};
|
|
20776
|
+
var defaultRoundingMethod = "trunc";
|
|
20777
|
+
function getRoundingMethod(method) {
|
|
20778
|
+
return method ? roundingMap[method] : roundingMap[defaultRoundingMethod];
|
|
20779
|
+
}
|
|
20780
|
+
function startOfMinute(dirtyDate) {
|
|
20781
|
+
requiredArgs(1, arguments);
|
|
20782
|
+
var date = toDate(dirtyDate);
|
|
20783
|
+
date.setSeconds(0, 0);
|
|
20784
|
+
return date;
|
|
20785
|
+
}
|
|
20786
|
+
function roundToNearestMinutes(dirtyDate, options) {
|
|
20787
|
+
var _options$nearestTo;
|
|
20788
|
+
if (arguments.length < 1) {
|
|
20789
|
+
throw new TypeError("1 argument required, but only none provided present");
|
|
20790
|
+
}
|
|
20791
|
+
var nearestTo = toInteger((_options$nearestTo = options === null || options === void 0 ? void 0 : options.nearestTo) !== null && _options$nearestTo !== void 0 ? _options$nearestTo : 1);
|
|
20792
|
+
if (nearestTo < 1 || nearestTo > 30) {
|
|
20793
|
+
throw new RangeError("`options.nearestTo` must be between 1 and 30");
|
|
20794
|
+
}
|
|
20795
|
+
var date = toDate(dirtyDate);
|
|
20796
|
+
var seconds = date.getSeconds();
|
|
20797
|
+
var minutes = date.getMinutes() + seconds / 60;
|
|
20798
|
+
var roundingMethod = getRoundingMethod(options === null || options === void 0 ? void 0 : options.roundingMethod);
|
|
20799
|
+
var roundedMinutes = roundingMethod(minutes / nearestTo) * nearestTo;
|
|
20800
|
+
var remainderMinutes = minutes % nearestTo;
|
|
20801
|
+
var addedMinutes = Math.round(remainderMinutes / nearestTo) * nearestTo;
|
|
20802
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), roundedMinutes + addedMinutes);
|
|
20803
|
+
}
|
|
20804
|
+
const millisecond = 1;
|
|
20805
|
+
const second = 1e3;
|
|
20806
|
+
const minute = 60 * second;
|
|
20807
|
+
const hour = 60 * minute;
|
|
20808
|
+
const day = 24 * hour;
|
|
20809
|
+
const week = 7 * day;
|
|
20810
|
+
const month = 30 * day;
|
|
20811
|
+
const year = 365 * day;
|
|
20812
|
+
const duration = {
|
|
20813
|
+
millisecond,
|
|
20814
|
+
second,
|
|
20815
|
+
minute,
|
|
20816
|
+
hour,
|
|
20817
|
+
day,
|
|
20818
|
+
week,
|
|
20819
|
+
month,
|
|
20820
|
+
year
|
|
20821
|
+
};
|
|
20822
|
+
function filterDataByType(datas, type) {
|
|
20823
|
+
return datas.filter((_) => _.type === type);
|
|
20824
|
+
}
|
|
20825
|
+
function filterDataByTime(datas, start, end) {
|
|
20826
|
+
const startTime = start.getTime();
|
|
20827
|
+
const endTime = end.getTime();
|
|
20828
|
+
return datas.map((data) => ({
|
|
20829
|
+
...data,
|
|
20830
|
+
points: data.points.filter(
|
|
20831
|
+
([timestamp]) => timestamp >= startTime && timestamp < endTime
|
|
20832
|
+
)
|
|
20833
|
+
})).filter(({ points }) => points.length > 0);
|
|
20834
|
+
}
|
|
20835
|
+
function fork(_) {
|
|
20836
|
+
return void 0;
|
|
20837
|
+
}
|
|
20838
|
+
class StoreCache {
|
|
20839
|
+
constructor({
|
|
20840
|
+
capacity,
|
|
20841
|
+
timeout
|
|
20842
|
+
} = {}) {
|
|
20843
|
+
__publicField(this, "entries", /* @__PURE__ */ new Map());
|
|
20844
|
+
__publicField(this, "metadata", /* @__PURE__ */ new Map());
|
|
20845
|
+
__publicField(this, "capacity");
|
|
20846
|
+
__publicField(this, "timeout");
|
|
20847
|
+
this.capacity = capacity || 1e4;
|
|
20848
|
+
this.timeout = timeout || duration.minute;
|
|
20849
|
+
}
|
|
20850
|
+
get(key, generator) {
|
|
20851
|
+
const cacheKey = this.keyToCacheKey(key);
|
|
20852
|
+
const entry = this.entries.get(cacheKey);
|
|
20853
|
+
const metadata = this.metadata.get(cacheKey);
|
|
20854
|
+
if ((entry === void 0 || metadata && (metadata == null ? void 0 : metadata.expiration.getTime()) < Date.now()) && !(metadata == null ? void 0 : metadata.generating) && generator) {
|
|
20855
|
+
this.generate(key, generator());
|
|
20856
|
+
}
|
|
20857
|
+
if (entry === void 0 && metadata && metadata.lastValue !== void 0) {
|
|
20858
|
+
return metadata.lastValue;
|
|
20859
|
+
}
|
|
20860
|
+
return entry;
|
|
20861
|
+
}
|
|
20862
|
+
set(key, value) {
|
|
20863
|
+
const cacheKey = this.keyToCacheKey(key);
|
|
20864
|
+
this.metadata.set(cacheKey, {
|
|
20865
|
+
generating: false,
|
|
20866
|
+
expiration: new Date(Date.now() + this.timeout),
|
|
20867
|
+
lastValue: value
|
|
20868
|
+
});
|
|
20869
|
+
this.entries.set(cacheKey, value);
|
|
20870
|
+
if (this.metadata.size > this.capacity) {
|
|
20871
|
+
this.deleteOldestEntry();
|
|
20872
|
+
}
|
|
20873
|
+
}
|
|
20874
|
+
clear() {
|
|
20875
|
+
this.entries.clear();
|
|
20876
|
+
[...this.metadata.values()].forEach((value) => value.generating = false);
|
|
20877
|
+
}
|
|
20878
|
+
clearKey(key) {
|
|
20879
|
+
this.metadata.delete(key);
|
|
20880
|
+
this.entries.delete(key);
|
|
20881
|
+
}
|
|
20882
|
+
keyToCacheKey(key) {
|
|
20883
|
+
return JSON.stringify(key);
|
|
20884
|
+
}
|
|
20885
|
+
deleteOldestEntry() {
|
|
20886
|
+
if (this.metadata.size < 1) {
|
|
20887
|
+
return;
|
|
20888
|
+
}
|
|
20889
|
+
const [key] = [...this.metadata.entries()].reduce(
|
|
20890
|
+
([oldestKey, oldestEntry], [thisKey, entry]) => entry.expiration.getTime() < oldestEntry.expiration.getTime() ? [thisKey, entry] : [oldestKey, oldestEntry]
|
|
20891
|
+
);
|
|
20892
|
+
this.clearKey(key);
|
|
20893
|
+
}
|
|
20894
|
+
generate(key, promise) {
|
|
20895
|
+
const cacheKey = this.keyToCacheKey(key);
|
|
20896
|
+
const existingMetadata = this.metadata.get(cacheKey) || {};
|
|
20897
|
+
this.metadata.set(cacheKey, {
|
|
20898
|
+
...existingMetadata,
|
|
20899
|
+
generating: true,
|
|
20900
|
+
expiration: new Date(Date.now() + this.timeout)
|
|
20901
|
+
});
|
|
20902
|
+
setTimeout(() => {
|
|
20903
|
+
fork(
|
|
20904
|
+
promise.then((value) => {
|
|
20905
|
+
const metadata = this.metadata.get(cacheKey);
|
|
20906
|
+
const canceled = !(metadata == null ? void 0 : metadata.generating);
|
|
20907
|
+
if (!canceled) {
|
|
20908
|
+
this.set(key, value);
|
|
20909
|
+
}
|
|
20910
|
+
})
|
|
20911
|
+
);
|
|
20912
|
+
}, 0);
|
|
20913
|
+
}
|
|
20914
|
+
}
|
|
20915
|
+
class QueryStore {
|
|
20916
|
+
constructor() {
|
|
20917
|
+
__publicField(this, "queryStoreCache", new StoreCache({
|
|
20918
|
+
capacity: 1e4,
|
|
20919
|
+
timeout: 20 * duration.second
|
|
20920
|
+
}));
|
|
20921
|
+
__publicField(this, "liveQueryStoreCache", new StoreCache({
|
|
20922
|
+
capacity: 1e4,
|
|
20923
|
+
timeout: 200 * duration.millisecond
|
|
20924
|
+
}));
|
|
20925
|
+
}
|
|
20926
|
+
moduleQuery(filter, name, type, start, end, latestOnly = false) {
|
|
20927
|
+
const q = {
|
|
20928
|
+
...filter,
|
|
20929
|
+
names: [name],
|
|
20930
|
+
types: [type]
|
|
20931
|
+
};
|
|
20932
|
+
const data = this.query(q, start, end, latestOnly);
|
|
20933
|
+
if (data === void 0 || data === "too much data") {
|
|
20934
|
+
return data;
|
|
20935
|
+
}
|
|
20936
|
+
return filterDataByType(data, type);
|
|
20937
|
+
}
|
|
20938
|
+
query(filter, start, end, latestOnly = false) {
|
|
20939
|
+
const q = {
|
|
20940
|
+
...filter,
|
|
20941
|
+
start: startOfMinute(start).toISOString(),
|
|
20942
|
+
end: latestOnly ? end.toISOString() : addMinutes(roundToNearestMinutes(end), 1).toISOString(),
|
|
20943
|
+
latestOnly
|
|
20944
|
+
};
|
|
20945
|
+
const isLive = end > addSeconds(new Date(), -20);
|
|
20946
|
+
let data;
|
|
20947
|
+
if (isLive) {
|
|
20948
|
+
data = this.liveQueryCache(q);
|
|
20949
|
+
} else {
|
|
20950
|
+
data = this.queryCache(q);
|
|
20951
|
+
}
|
|
20952
|
+
if (!data || data === "too much data") {
|
|
20953
|
+
return data;
|
|
20954
|
+
}
|
|
20955
|
+
if (latestOnly) {
|
|
20956
|
+
return data;
|
|
20957
|
+
}
|
|
20958
|
+
return filterDataByTime(data, start, end);
|
|
20959
|
+
}
|
|
20960
|
+
queryCache(query) {
|
|
20961
|
+
return this.queryStoreCache.get(query, async () => {
|
|
20962
|
+
try {
|
|
20963
|
+
return await Fleet$1.queryTelemetry(query);
|
|
20964
|
+
} catch (error) {
|
|
20965
|
+
throw error;
|
|
20364
20966
|
}
|
|
20365
20967
|
});
|
|
20366
|
-
console.log(onDemandList);
|
|
20367
|
-
const data = await result.json();
|
|
20368
|
-
let streamNames = data.items.filter((_) => !disabledList.includes(_)).map((_) => ({ name: _, onDemand: onDemandList.includes(_) }));
|
|
20369
|
-
return streamNames;
|
|
20370
20968
|
}
|
|
20371
|
-
|
|
20372
|
-
|
|
20373
|
-
|
|
20374
|
-
|
|
20375
|
-
|
|
20376
|
-
|
|
20377
|
-
message,
|
|
20378
|
-
interventionType,
|
|
20379
|
-
time: new Date().toISOString(),
|
|
20380
|
-
deviceId: this.id,
|
|
20381
|
-
tags,
|
|
20382
|
-
data: interventionRequest
|
|
20383
|
-
}),
|
|
20384
|
-
headers: {
|
|
20385
|
-
"Content-Type": "application/json",
|
|
20386
|
-
Authorization: "Bearer " + Authentication.token
|
|
20387
|
-
}
|
|
20969
|
+
liveQueryCache(query) {
|
|
20970
|
+
return this.liveQueryStoreCache.get(query, async () => {
|
|
20971
|
+
try {
|
|
20972
|
+
return await Fleet$1.queryTelemetry(query);
|
|
20973
|
+
} catch (error) {
|
|
20974
|
+
throw error;
|
|
20388
20975
|
}
|
|
20389
|
-
);
|
|
20390
|
-
const interventionJson = await intervention.json();
|
|
20391
|
-
return interventionJson;
|
|
20976
|
+
});
|
|
20392
20977
|
}
|
|
20393
|
-
|
|
20978
|
+
}
|
|
20979
|
+
const queryStore = new QueryStore();
|
|
20980
|
+
class App {
|
|
20981
|
+
static sendAppMessage(message) {
|
|
20982
|
+
window.parent.postMessage(message, "*");
|
|
20983
|
+
}
|
|
20984
|
+
static getCurrentModuleContext() {
|
|
20985
|
+
let urlParams2 = new URLSearchParams("");
|
|
20986
|
+
if (typeof window !== "undefined") {
|
|
20987
|
+
urlParams2 = new URLSearchParams(window.location.search);
|
|
20988
|
+
}
|
|
20989
|
+
const moduleName2 = urlParams2.get("module");
|
|
20990
|
+
return moduleName2;
|
|
20991
|
+
}
|
|
20992
|
+
static async getCurrentModuleConfiguration() {
|
|
20993
|
+
let urlParams2 = new URLSearchParams("");
|
|
20994
|
+
if (typeof window !== "undefined") {
|
|
20995
|
+
urlParams2 = new URLSearchParams(window.location.search);
|
|
20996
|
+
}
|
|
20997
|
+
const configurationId = urlParams2.get("configuration");
|
|
20998
|
+
if (configurationId === null || configurationId.trim() === "") {
|
|
20999
|
+
return void 0;
|
|
21000
|
+
}
|
|
20394
21001
|
const response = await fetch(
|
|
20395
|
-
`${FORMANT_API_URL}/v1/admin/
|
|
21002
|
+
`${FORMANT_API_URL}/v1/admin/module-configurations/` + configurationId,
|
|
20396
21003
|
{
|
|
20397
|
-
method: "POST",
|
|
20398
|
-
body: JSON.stringify({
|
|
20399
|
-
interventionId,
|
|
20400
|
-
interventionType,
|
|
20401
|
-
data
|
|
20402
|
-
}),
|
|
20403
21004
|
headers: {
|
|
20404
21005
|
"Content-Type": "application/json",
|
|
20405
21006
|
Authorization: "Bearer " + Authentication.token
|
|
20406
21007
|
}
|
|
20407
21008
|
}
|
|
20408
21009
|
);
|
|
20409
|
-
const
|
|
20410
|
-
return
|
|
21010
|
+
const moduleConfiguration = await response.json();
|
|
21011
|
+
return moduleConfiguration.configuration;
|
|
20411
21012
|
}
|
|
20412
|
-
|
|
20413
|
-
|
|
20414
|
-
constructor(peerUrl) {
|
|
20415
|
-
__publicField(this, "rtcClient");
|
|
20416
|
-
__publicField(this, "remoteDevicePeerId");
|
|
20417
|
-
__publicField(this, "realtimeListeners", []);
|
|
20418
|
-
__publicField(this, "id");
|
|
20419
|
-
__publicField(this, "handleMessage", (peerId, message) => {
|
|
20420
|
-
this.realtimeListeners.forEach((_) => _(peerId, message));
|
|
20421
|
-
});
|
|
20422
|
-
this.peerUrl = peerUrl;
|
|
21013
|
+
static isModule() {
|
|
21014
|
+
return this.getCurrentModuleContext() !== null;
|
|
20423
21015
|
}
|
|
20424
|
-
|
|
20425
|
-
|
|
20426
|
-
|
|
20427
|
-
|
|
20428
|
-
return entries.map(([stream, latestValue]) => {
|
|
20429
|
-
const v = {
|
|
20430
|
-
deviceId: this.id,
|
|
20431
|
-
streamName: stream,
|
|
20432
|
-
streamType: "json",
|
|
20433
|
-
currentValue: latestValue,
|
|
20434
|
-
currentValueTime: latestValue.timestamp,
|
|
20435
|
-
tags: {}
|
|
20436
|
-
};
|
|
20437
|
-
return v;
|
|
21016
|
+
static goToTime(date) {
|
|
21017
|
+
this.sendAppMessage({
|
|
21018
|
+
type: "go_to_time",
|
|
21019
|
+
time: date.getTime()
|
|
20438
21020
|
});
|
|
20439
21021
|
}
|
|
20440
|
-
|
|
20441
|
-
|
|
20442
|
-
|
|
20443
|
-
|
|
21022
|
+
static goToDevice(deviceId) {
|
|
21023
|
+
this.sendAppMessage({
|
|
21024
|
+
type: "go_to_device",
|
|
21025
|
+
deviceId
|
|
21026
|
+
});
|
|
20444
21027
|
}
|
|
20445
|
-
|
|
20446
|
-
|
|
20447
|
-
const cfg = await result.json();
|
|
20448
|
-
return cfg.agent_config.document;
|
|
21028
|
+
static showMessage(message) {
|
|
21029
|
+
this.sendAppMessage({ type: "show_message", message });
|
|
20449
21030
|
}
|
|
20450
|
-
|
|
20451
|
-
|
|
20452
|
-
|
|
20453
|
-
|
|
20454
|
-
throw new Error(`Realtime connection hasn't been started`);
|
|
21031
|
+
static requestModuleData() {
|
|
21032
|
+
const moduleName2 = this.getCurrentModuleContext();
|
|
21033
|
+
if (!moduleName2) {
|
|
21034
|
+
throw new Error("No module context");
|
|
20455
21035
|
}
|
|
21036
|
+
this.sendAppMessage({
|
|
21037
|
+
type: "request_module_data",
|
|
21038
|
+
module: moduleName2
|
|
21039
|
+
});
|
|
20456
21040
|
}
|
|
20457
|
-
|
|
20458
|
-
|
|
20459
|
-
|
|
20460
|
-
|
|
20461
|
-
throw new Error(`Realtime connection hasn't been started`);
|
|
21041
|
+
static setModuleDateTimeRange(beforeInMilliseconds, afterInMilliseconds) {
|
|
21042
|
+
const moduleName2 = this.getCurrentModuleContext();
|
|
21043
|
+
if (!moduleName2) {
|
|
21044
|
+
throw new Error("No module context");
|
|
20462
21045
|
}
|
|
21046
|
+
this.sendAppMessage({
|
|
21047
|
+
type: "set_module_data_time_range",
|
|
21048
|
+
module: moduleName2,
|
|
21049
|
+
before: beforeInMilliseconds,
|
|
21050
|
+
after: afterInMilliseconds || 0
|
|
21051
|
+
});
|
|
20463
21052
|
}
|
|
20464
|
-
|
|
20465
|
-
|
|
20466
|
-
|
|
20467
|
-
|
|
20468
|
-
receive: this.handleMessage,
|
|
20469
|
-
sessionType
|
|
20470
|
-
});
|
|
20471
|
-
await rtcClient.connectLan(this.peerUrl);
|
|
20472
|
-
while (rtcClient.getConnectionStatus(this.peerUrl) !== "connected") {
|
|
20473
|
-
await delay(100);
|
|
20474
|
-
}
|
|
20475
|
-
this.rtcClient = rtcClient;
|
|
20476
|
-
} else {
|
|
20477
|
-
throw new Error(
|
|
20478
|
-
`Already created realtime connection to device ${this.id}`
|
|
20479
|
-
);
|
|
21053
|
+
static refreshAuthToken() {
|
|
21054
|
+
const moduleName2 = this.getCurrentModuleContext();
|
|
21055
|
+
if (!moduleName2) {
|
|
21056
|
+
throw new Error("No module context");
|
|
20480
21057
|
}
|
|
21058
|
+
this.sendAppMessage({
|
|
21059
|
+
type: "refresh_auth_token",
|
|
21060
|
+
module: moduleName2
|
|
21061
|
+
});
|
|
20481
21062
|
}
|
|
20482
|
-
|
|
20483
|
-
this.
|
|
20484
|
-
|
|
20485
|
-
|
|
20486
|
-
const i = this.realtimeListeners.indexOf(listener);
|
|
20487
|
-
if (i === -1) {
|
|
20488
|
-
throw new Error("Could not find realtime listener to remove");
|
|
21063
|
+
static sendChannelData(channel, data) {
|
|
21064
|
+
const moduleName2 = this.getCurrentModuleContext();
|
|
21065
|
+
if (!moduleName2) {
|
|
21066
|
+
throw new Error("No module context");
|
|
20489
21067
|
}
|
|
20490
|
-
this.
|
|
21068
|
+
this.sendAppMessage({
|
|
21069
|
+
type: "send_channel_data",
|
|
21070
|
+
source: moduleName2,
|
|
21071
|
+
channel,
|
|
21072
|
+
data
|
|
21073
|
+
});
|
|
20491
21074
|
}
|
|
20492
|
-
|
|
20493
|
-
|
|
20494
|
-
|
|
20495
|
-
|
|
20496
|
-
for (const _ of (_a = document2.teleop.hardwareStreams) != null ? _a : []) {
|
|
20497
|
-
if (_.rtcStreamType === "h264-video-frame") {
|
|
20498
|
-
streams.push({
|
|
20499
|
-
name: _.name
|
|
20500
|
-
});
|
|
20501
|
-
}
|
|
21075
|
+
static setupModuleMenus(menus) {
|
|
21076
|
+
const moduleName2 = this.getCurrentModuleContext();
|
|
21077
|
+
if (!moduleName2) {
|
|
21078
|
+
throw new Error("No module context");
|
|
20502
21079
|
}
|
|
20503
|
-
|
|
20504
|
-
|
|
20505
|
-
|
|
20506
|
-
|
|
20507
|
-
|
|
21080
|
+
this.sendAppMessage({
|
|
21081
|
+
type: "setup_module_menus",
|
|
21082
|
+
module: moduleName2,
|
|
21083
|
+
menus
|
|
21084
|
+
});
|
|
21085
|
+
}
|
|
21086
|
+
static addMenuListener(handler) {
|
|
21087
|
+
window.addEventListener("message", (event) => {
|
|
21088
|
+
const msg = event.data;
|
|
21089
|
+
if (msg.type === "module_menu_item_clicked") {
|
|
21090
|
+
handler(msg.menu);
|
|
20508
21091
|
}
|
|
20509
|
-
}
|
|
20510
|
-
|
|
20511
|
-
|
|
20512
|
-
|
|
20513
|
-
|
|
20514
|
-
|
|
21092
|
+
});
|
|
21093
|
+
}
|
|
21094
|
+
static addAccessTokenRefreshListener(handler) {
|
|
21095
|
+
window.addEventListener("message", (event) => {
|
|
21096
|
+
const msg = event.data;
|
|
21097
|
+
if (msg.type === "auth_token") {
|
|
21098
|
+
handler(msg.token);
|
|
20515
21099
|
}
|
|
20516
|
-
}
|
|
20517
|
-
return streams;
|
|
21100
|
+
});
|
|
20518
21101
|
}
|
|
20519
|
-
|
|
20520
|
-
|
|
20521
|
-
|
|
20522
|
-
|
|
20523
|
-
|
|
20524
|
-
|
|
20525
|
-
|
|
20526
|
-
|
|
20527
|
-
|
|
20528
|
-
|
|
20529
|
-
|
|
20530
|
-
|
|
20531
|
-
|
|
20532
|
-
|
|
20533
|
-
|
|
20534
|
-
|
|
21102
|
+
static addModuleDataListener(handler) {
|
|
21103
|
+
const moduleName2 = this.getCurrentModuleContext();
|
|
21104
|
+
if (moduleName2) {
|
|
21105
|
+
this.sendAppMessage({ type: "request_module_data", module: moduleName2 });
|
|
21106
|
+
}
|
|
21107
|
+
window.addEventListener("message", (event) => {
|
|
21108
|
+
const msg = event.data;
|
|
21109
|
+
if (msg.type === "module_data") {
|
|
21110
|
+
handler({
|
|
21111
|
+
streams: msg.streams,
|
|
21112
|
+
time: msg.time,
|
|
21113
|
+
queryRange: msg.queryRange
|
|
21114
|
+
});
|
|
21115
|
+
}
|
|
21116
|
+
});
|
|
21117
|
+
}
|
|
21118
|
+
static addStreamListener(streamName, streamType, handler) {
|
|
21119
|
+
const listener = (event) => {
|
|
21120
|
+
const msg = event.data;
|
|
21121
|
+
if (msg.type === "module_data") {
|
|
21122
|
+
const { start, end } = msg.queryRange;
|
|
21123
|
+
handler(
|
|
21124
|
+
queryStore.moduleQuery(
|
|
21125
|
+
{},
|
|
21126
|
+
streamName,
|
|
21127
|
+
streamType,
|
|
21128
|
+
new Date(start),
|
|
21129
|
+
new Date(end),
|
|
21130
|
+
false
|
|
21131
|
+
)
|
|
20535
21132
|
);
|
|
20536
21133
|
}
|
|
20537
|
-
}
|
|
20538
|
-
|
|
21134
|
+
};
|
|
21135
|
+
window.addEventListener("message", listener);
|
|
21136
|
+
return () => window.removeEventListener("message", listener);
|
|
20539
21137
|
}
|
|
20540
|
-
|
|
20541
|
-
|
|
20542
|
-
|
|
20543
|
-
|
|
20544
|
-
|
|
20545
|
-
|
|
20546
|
-
client.controlRemoteStream(defined(devicePeer).id, {
|
|
20547
|
-
streamName: stream.name,
|
|
20548
|
-
enable: true,
|
|
20549
|
-
pipeline: "rtc"
|
|
21138
|
+
static addModuleConfigurationListener(handler) {
|
|
21139
|
+
window.addEventListener("message", (event) => {
|
|
21140
|
+
const msg = event.data;
|
|
21141
|
+
if (msg.type === "module_configuration") {
|
|
21142
|
+
handler(msg);
|
|
21143
|
+
}
|
|
20550
21144
|
});
|
|
20551
21145
|
}
|
|
20552
|
-
|
|
20553
|
-
|
|
20554
|
-
|
|
20555
|
-
"
|
|
20556
|
-
|
|
20557
|
-
|
|
20558
|
-
|
|
20559
|
-
|
|
20560
|
-
|
|
20561
|
-
pipeline: "rtc"
|
|
21146
|
+
static addChannelDataListener(channel, handler) {
|
|
21147
|
+
window.addEventListener("message", (event) => {
|
|
21148
|
+
const msg = event.data;
|
|
21149
|
+
if (msg.type === "channel_data" && msg.channel === channel) {
|
|
21150
|
+
handler({
|
|
21151
|
+
source: msg.source,
|
|
21152
|
+
data: msg.data
|
|
21153
|
+
});
|
|
21154
|
+
}
|
|
20562
21155
|
});
|
|
20563
21156
|
}
|
|
20564
|
-
|
|
20565
|
-
|
|
20566
|
-
|
|
20567
|
-
|
|
20568
|
-
|
|
20569
|
-
const devicePeer = await this.getRemotePeer();
|
|
20570
|
-
client.controlRemoteStream(defined(devicePeer).id, {
|
|
20571
|
-
streamName: stream.name,
|
|
20572
|
-
enable: true,
|
|
20573
|
-
pipeline: "rtc"
|
|
21157
|
+
static requestOverviewDevices(handler) {
|
|
21158
|
+
this.sendAppMessage({ type: "request_devices" });
|
|
21159
|
+
window.addEventListener("message", (event) => {
|
|
21160
|
+
const msg = event.data;
|
|
21161
|
+
handler(msg);
|
|
20574
21162
|
});
|
|
20575
21163
|
}
|
|
20576
|
-
|
|
20577
|
-
|
|
20578
|
-
|
|
20579
|
-
|
|
20580
|
-
|
|
20581
|
-
|
|
20582
|
-
|
|
20583
|
-
|
|
20584
|
-
|
|
20585
|
-
|
|
20586
|
-
|
|
21164
|
+
}
|
|
21165
|
+
const _Authentication = class {
|
|
21166
|
+
static async login(email, password) {
|
|
21167
|
+
try {
|
|
21168
|
+
const result = await fetch(`${FORMANT_API_URL}/v1/admin/auth/login`, {
|
|
21169
|
+
method: "POST",
|
|
21170
|
+
body: JSON.stringify({ email, password }),
|
|
21171
|
+
headers: {
|
|
21172
|
+
"Content-Type": "application/json"
|
|
21173
|
+
}
|
|
21174
|
+
});
|
|
21175
|
+
const auth = await result.json();
|
|
21176
|
+
if (result.status !== 200) {
|
|
21177
|
+
throw new Error(auth.message);
|
|
21178
|
+
}
|
|
21179
|
+
await _Authentication.loginWithToken(
|
|
21180
|
+
auth.authentication.accessToken,
|
|
21181
|
+
auth.authentication.refreshToken
|
|
21182
|
+
);
|
|
21183
|
+
return auth.authentication;
|
|
21184
|
+
} catch (e) {
|
|
21185
|
+
_Authentication.waitingForAuth.forEach((_) => _(false));
|
|
21186
|
+
_Authentication.waitingForAuth = [];
|
|
21187
|
+
return Promise.reject(e);
|
|
21188
|
+
}
|
|
20587
21189
|
}
|
|
20588
|
-
async
|
|
20589
|
-
const
|
|
20590
|
-
|
|
20591
|
-
|
|
20592
|
-
|
|
20593
|
-
|
|
20594
|
-
|
|
20595
|
-
|
|
20596
|
-
|
|
20597
|
-
|
|
20598
|
-
|
|
21190
|
+
static async loginWithToken(token, refreshToken) {
|
|
21191
|
+
const tokenData = JSON.parse(atob(token.split(".")[1]));
|
|
21192
|
+
try {
|
|
21193
|
+
let userId;
|
|
21194
|
+
_Authentication.isShareToken = tokenData["formant:claims"] && tokenData["formant:claims"].type == "share";
|
|
21195
|
+
if (tokenData["formant:claims"]) {
|
|
21196
|
+
_Authentication.currentOrganization = tokenData["formant:claims"].organizationId;
|
|
21197
|
+
}
|
|
21198
|
+
if (tokenData["custom:organization_id"]) {
|
|
21199
|
+
_Authentication.currentOrganization = tokenData["custom:organization_id"];
|
|
21200
|
+
}
|
|
21201
|
+
if (!_Authentication.isShareToken) {
|
|
21202
|
+
userId = tokenData.sub;
|
|
21203
|
+
}
|
|
21204
|
+
if (tokenData["formant:claims"] && tokenData["formant:claims"].userId) {
|
|
21205
|
+
userId = tokenData["formant:claims"].userId;
|
|
21206
|
+
}
|
|
21207
|
+
if (userId) {
|
|
21208
|
+
const result = await fetch(
|
|
21209
|
+
`${FORMANT_API_URL}/v1/admin/users/${userId}`,
|
|
21210
|
+
{
|
|
21211
|
+
method: "GET",
|
|
21212
|
+
headers: {
|
|
21213
|
+
"Content-Type": "application/json",
|
|
21214
|
+
Authorization: "Bearer " + token
|
|
21215
|
+
}
|
|
21216
|
+
}
|
|
21217
|
+
);
|
|
21218
|
+
const data = await result.json();
|
|
21219
|
+
if (result.status !== 200) {
|
|
21220
|
+
throw new Error(data.message);
|
|
21221
|
+
}
|
|
21222
|
+
_Authentication.currentUser = data;
|
|
21223
|
+
}
|
|
21224
|
+
_Authentication.token = token;
|
|
21225
|
+
_Authentication.waitingForAuth.forEach((_) => _(true));
|
|
21226
|
+
} catch (e) {
|
|
21227
|
+
console.error(e);
|
|
21228
|
+
_Authentication.waitingForAuth.forEach((_) => _(false));
|
|
21229
|
+
}
|
|
21230
|
+
_Authentication.waitingForAuth = [];
|
|
21231
|
+
if (refreshToken) {
|
|
21232
|
+
_Authentication.refreshToken = refreshToken;
|
|
21233
|
+
setInterval(async () => {
|
|
21234
|
+
if (_Authentication.refreshToken) {
|
|
21235
|
+
const result = await fetch(
|
|
21236
|
+
`${FORMANT_API_URL}/v1/admin/auth/refresh`,
|
|
21237
|
+
{
|
|
21238
|
+
method: "POST",
|
|
21239
|
+
headers: {
|
|
21240
|
+
"Content-Type": "application/json"
|
|
21241
|
+
},
|
|
21242
|
+
body: JSON.stringify({
|
|
21243
|
+
refreshToken: _Authentication.refreshToken
|
|
21244
|
+
})
|
|
21245
|
+
}
|
|
21246
|
+
);
|
|
21247
|
+
const refreshData = await result.json();
|
|
21248
|
+
_Authentication.token = refreshData.authentication.accessToken;
|
|
21249
|
+
}
|
|
21250
|
+
}, 1e3 * 60 * 60);
|
|
21251
|
+
}
|
|
20599
21252
|
}
|
|
20600
|
-
|
|
20601
|
-
|
|
20602
|
-
this.rtcClient,
|
|
20603
|
-
"Realtime connection has not been started"
|
|
20604
|
-
);
|
|
20605
|
-
const devicePeer = await this.getRemotePeer();
|
|
20606
|
-
client.controlRemoteStream(defined(devicePeer).id, {
|
|
20607
|
-
streamName,
|
|
20608
|
-
enablePriorityUpload: false,
|
|
20609
|
-
pipeline: "telemetry"
|
|
20610
|
-
});
|
|
21253
|
+
static isAuthenticated() {
|
|
21254
|
+
return _Authentication.token !== void 0;
|
|
20611
21255
|
}
|
|
20612
|
-
|
|
20613
|
-
return
|
|
20614
|
-
id: this.peerUrl,
|
|
20615
|
-
organizationId: "",
|
|
20616
|
-
deviceId: this.id,
|
|
20617
|
-
capabilities: [],
|
|
20618
|
-
capabilitySet: {}
|
|
20619
|
-
};
|
|
21256
|
+
static getCurrentUser() {
|
|
21257
|
+
return _Authentication.currentUser;
|
|
20620
21258
|
}
|
|
20621
|
-
async
|
|
20622
|
-
if (
|
|
20623
|
-
|
|
21259
|
+
static async waitTilAuthenticated() {
|
|
21260
|
+
if (_Authentication.token !== void 0) {
|
|
21261
|
+
return true;
|
|
20624
21262
|
} else {
|
|
20625
|
-
|
|
21263
|
+
return new Promise((resolve) => {
|
|
21264
|
+
_Authentication.waitingForAuth.push(function(result) {
|
|
21265
|
+
resolve(result);
|
|
21266
|
+
});
|
|
21267
|
+
});
|
|
20626
21268
|
}
|
|
20627
21269
|
}
|
|
20628
|
-
async
|
|
20629
|
-
|
|
20630
|
-
this.
|
|
20631
|
-
"Realtime connection has not been started"
|
|
20632
|
-
);
|
|
20633
|
-
const devicePeer = await this.getRemotePeer();
|
|
20634
|
-
const p = await new Promise((resolve) => {
|
|
20635
|
-
client.createCustomDataChannel(
|
|
20636
|
-
defined(devicePeer).id,
|
|
20637
|
-
channelName,
|
|
20638
|
-
{
|
|
20639
|
-
ordered: true,
|
|
20640
|
-
...rtcConfig
|
|
20641
|
-
},
|
|
20642
|
-
false,
|
|
20643
|
-
(_peerId, channel) => {
|
|
20644
|
-
const dataChannel = new DataChannel(channel);
|
|
20645
|
-
resolve(dataChannel);
|
|
20646
|
-
}
|
|
20647
|
-
);
|
|
21270
|
+
static async listenForRefresh() {
|
|
21271
|
+
App.addAccessTokenRefreshListener((token) => {
|
|
21272
|
+
this.loginWithToken(token);
|
|
20648
21273
|
});
|
|
20649
|
-
|
|
20650
|
-
|
|
20651
|
-
|
|
20652
|
-
createCustomRequestDataChannel(channelName, timeout = 3e3) {
|
|
20653
|
-
return new TextRequestDataChannel(this, channelName, timeout);
|
|
20654
|
-
}
|
|
20655
|
-
createCustomBinaryRequestDataChannel(channelName, timeout = 3e3) {
|
|
20656
|
-
return new BinaryRequestDataChannel(this, channelName, timeout);
|
|
21274
|
+
setInterval(async () => {
|
|
21275
|
+
App.refreshAuthToken();
|
|
21276
|
+
}, 1e3 * 60 * 60);
|
|
20657
21277
|
}
|
|
20658
|
-
}
|
|
21278
|
+
};
|
|
21279
|
+
let Authentication = _Authentication;
|
|
21280
|
+
__publicField(Authentication, "token");
|
|
21281
|
+
__publicField(Authentication, "refreshToken");
|
|
21282
|
+
__publicField(Authentication, "currentUser");
|
|
21283
|
+
__publicField(Authentication, "currentOrganization");
|
|
21284
|
+
__publicField(Authentication, "isShareToken", false);
|
|
21285
|
+
__publicField(Authentication, "defaultDeviceId");
|
|
21286
|
+
__publicField(Authentication, "waitingForAuth", []);
|
|
20659
21287
|
const _Fleet = class {
|
|
20660
21288
|
static async setDefaultDevice(deviceId) {
|
|
20661
21289
|
_Fleet.defaultDeviceId = deviceId;
|
|
@@ -21159,9 +21787,6 @@ function combineNumericSetAggregates(e1, e2) {
|
|
|
21159
21787
|
function stringToArrayBuffer(input) {
|
|
21160
21788
|
return Uint8Array.from(atob(input), (_) => _.charCodeAt(0));
|
|
21161
21789
|
}
|
|
21162
|
-
function fork(_) {
|
|
21163
|
-
return void 0;
|
|
21164
|
-
}
|
|
21165
21790
|
function browser() {
|
|
21166
21791
|
const { userAgent } = navigator;
|
|
21167
21792
|
if (userAgent.includes("Firefox/")) {
|