@dronedeploy/rocos-js-sdk 4.4.2 → 4.4.4
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/cjs/api/StreamRegister.js +9 -3
- package/cjs/models/gamepad/GamepadConfig.d.ts +26 -0
- package/cjs/models/gamepad/GamepadConfig.js +11 -1
- package/cjs/services/BaseStreamService.d.ts +1 -1
- package/cjs/services/BaseStreamService.js +21 -2
- package/cjs/services/TelemetryService.js +21 -14
- package/esm/api/StreamRegister.js +9 -3
- package/esm/models/gamepad/GamepadConfig.d.ts +26 -0
- package/esm/models/gamepad/GamepadConfig.js +10 -0
- package/esm/services/BaseStreamService.d.ts +1 -1
- package/esm/services/BaseStreamService.js +21 -2
- package/esm/services/TelemetryService.js +22 -15
- package/package.json +8 -1
|
@@ -19,8 +19,10 @@ class StreamRegister {
|
|
|
19
19
|
}
|
|
20
20
|
addStream(stream) {
|
|
21
21
|
try {
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
// Stop any existing occupant of this key before overwriting it.
|
|
23
|
+
const existing = this.teleStreams.get(stream.identifier);
|
|
24
|
+
if (existing && existing !== stream) {
|
|
25
|
+
existing.stopStream();
|
|
24
26
|
}
|
|
25
27
|
this.teleStreams.set(stream.identifier, stream);
|
|
26
28
|
}
|
|
@@ -40,7 +42,11 @@ class StreamRegister {
|
|
|
40
42
|
removeStream(stream) {
|
|
41
43
|
try {
|
|
42
44
|
stream.stopStream();
|
|
43
|
-
this
|
|
45
|
+
// Only clear the entry if this instance still owns the key: a replacement may already hold it,
|
|
46
|
+
// and evicting that would leave subscribers forking duplicate streams for the scope.
|
|
47
|
+
if (this.teleStreams.get(stream.identifier) === stream) {
|
|
48
|
+
this.teleStreams.delete(stream.identifier);
|
|
49
|
+
}
|
|
44
50
|
}
|
|
45
51
|
catch (e) {
|
|
46
52
|
this.logger.error(`Failed to remove stream from list. identifier: ${stream.identifier}`, e);
|
|
@@ -31,6 +31,18 @@ export declare enum GamepadButtonType {
|
|
|
31
31
|
}
|
|
32
32
|
/** Per-axis tuning keyed by GamepadButtonType. Absent entirely on configs saved before axis tuning existed. */
|
|
33
33
|
export type GamepadAxisValueMap = Partial<Record<GamepadButtonType, number>>;
|
|
34
|
+
/** Same shape as GamepadAxisValueMap, keyed by any input — a modifier is usually a bumper or trigger, not an axis. */
|
|
35
|
+
export type GamepadInputValueMap = Partial<Record<GamepadButtonType, number>>;
|
|
36
|
+
/**
|
|
37
|
+
* Engage point for an input with no GamepadConfig.modifierThresholds entry. Exported so editor and
|
|
38
|
+
* interpreter agree: a modifier held at 0.5 in one and 0.75 in the other works on screen, not on the robot.
|
|
39
|
+
*/
|
|
40
|
+
export declare const DEFAULT_MODIFIER_THRESHOLD = 0.5;
|
|
41
|
+
/**
|
|
42
|
+
* Release point as a fraction of the engage threshold. Releasing at the engage value flaps the layer at
|
|
43
|
+
* frame rate while a trigger rests on the threshold. Exported for the same reason as the threshold.
|
|
44
|
+
*/
|
|
45
|
+
export declare const MODIFIER_RELEASE_FRACTION = 0.7;
|
|
34
46
|
export declare enum GamepadTriggerType {
|
|
35
47
|
ONCE = "once",
|
|
36
48
|
CONTINUOUSLY = "continuously"
|
|
@@ -54,6 +66,15 @@ export interface GamepadAction {
|
|
|
54
66
|
triggerType: GamepadTriggerType;
|
|
55
67
|
/** Milliseconds between repeats. Only meaningful for `continuously`. */
|
|
56
68
|
frequency: number;
|
|
69
|
+
/**
|
|
70
|
+
* Inputs that must be held for this action to fire. A set: unordered, and absent or empty is the base
|
|
71
|
+
* layer, eligible whatever is held — which is what leaves pre-modifier configs unaffected.
|
|
72
|
+
*
|
|
73
|
+
* Matched on `type` alone, since `text` is a display label and the same input reaches us as "LB" from
|
|
74
|
+
* one author and "L1" from another. Must not contain the action's own `source`, which the triggering
|
|
75
|
+
* press would satisfy on its own.
|
|
76
|
+
*/
|
|
77
|
+
modifiers?: GamepadButtonRef[];
|
|
57
78
|
}
|
|
58
79
|
export interface GamepadVariable {
|
|
59
80
|
name: string;
|
|
@@ -80,6 +101,11 @@ export interface GamepadConfig {
|
|
|
80
101
|
variables: GamepadVariable[];
|
|
81
102
|
axisDeadzones?: GamepadAxisValueMap;
|
|
82
103
|
axisExpos?: GamepadAxisValueMap;
|
|
104
|
+
/**
|
|
105
|
+
* Engage point per modifier input. Profile-level rather than per-action so one trigger cannot engage at
|
|
106
|
+
* two values. Absent entries use DEFAULT_MODIFIER_THRESHOLD.
|
|
107
|
+
*/
|
|
108
|
+
modifierThresholds?: GamepadInputValueMap;
|
|
83
109
|
}
|
|
84
110
|
/** One stored gamepad config, scoped to a specific physical controller model (GamepadConfig.gamepad). */
|
|
85
111
|
export type GamepadConfigItem = ConfigGroupItem<GamepadConfig>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.GamepadCommandVersion = exports.GamepadTriggerType = exports.GamepadButtonType = void 0;
|
|
3
|
+
exports.GamepadCommandVersion = exports.GamepadTriggerType = exports.MODIFIER_RELEASE_FRACTION = exports.DEFAULT_MODIFIER_THRESHOLD = exports.GamepadButtonType = void 0;
|
|
4
4
|
// Ref: https://github.com/alaingilbert/GamepadJs
|
|
5
5
|
var GamepadButtonType;
|
|
6
6
|
(function (GamepadButtonType) {
|
|
@@ -33,6 +33,16 @@ var GamepadButtonType;
|
|
|
33
33
|
GamepadButtonType[GamepadButtonType["SQUARE"] = 26] = "SQUARE";
|
|
34
34
|
GamepadButtonType[GamepadButtonType["TRIANGLE"] = 27] = "TRIANGLE";
|
|
35
35
|
})(GamepadButtonType || (exports.GamepadButtonType = GamepadButtonType = {}));
|
|
36
|
+
/**
|
|
37
|
+
* Engage point for an input with no GamepadConfig.modifierThresholds entry. Exported so editor and
|
|
38
|
+
* interpreter agree: a modifier held at 0.5 in one and 0.75 in the other works on screen, not on the robot.
|
|
39
|
+
*/
|
|
40
|
+
exports.DEFAULT_MODIFIER_THRESHOLD = 0.5;
|
|
41
|
+
/**
|
|
42
|
+
* Release point as a fraction of the engage threshold. Releasing at the engage value flaps the layer at
|
|
43
|
+
* frame rate while a trigger rests on the threshold. Exported for the same reason as the threshold.
|
|
44
|
+
*/
|
|
45
|
+
exports.MODIFIER_RELEASE_FRACTION = 0.7;
|
|
36
46
|
var GamepadTriggerType;
|
|
37
47
|
(function (GamepadTriggerType) {
|
|
38
48
|
GamepadTriggerType["ONCE"] = "once";
|
|
@@ -10,7 +10,7 @@ export declare abstract class BaseStreamService<T extends IBaseStream, C extends
|
|
|
10
10
|
protected constructor(name: string, config: IRocosSDKConfig);
|
|
11
11
|
getStatus(): boolean;
|
|
12
12
|
protected initStream(stream: T): Promise<void>;
|
|
13
|
-
protected createStreamFromConfig(identifier: string, config: C): Promise<{
|
|
13
|
+
protected createStreamFromConfig(identifier: string, config: C, onAcquire?: (stream: T) => void): Promise<{
|
|
14
14
|
stream: T;
|
|
15
15
|
isNew: boolean;
|
|
16
16
|
}>;
|
|
@@ -32,7 +32,7 @@ class BaseStreamService {
|
|
|
32
32
|
}
|
|
33
33
|
authService.startTokenRefreshChecker();
|
|
34
34
|
}
|
|
35
|
-
async createStreamFromConfig(identifier, config) {
|
|
35
|
+
async createStreamFromConfig(identifier, config, onAcquire) {
|
|
36
36
|
const identifierWithScope = StreamRegister_1.StreamRegister.getIdentifier(identifier, config.scope);
|
|
37
37
|
const streamRegister = StreamRegister_1.StreamRegister.getInstance();
|
|
38
38
|
let stream = streamRegister.getStream(identifierWithScope);
|
|
@@ -45,7 +45,26 @@ class BaseStreamService {
|
|
|
45
45
|
this.status$.next(msg);
|
|
46
46
|
});
|
|
47
47
|
streamRegister.addStream(stream);
|
|
48
|
-
|
|
48
|
+
}
|
|
49
|
+
// Acquire synchronously with the lookup/registration above, before any await. A stream is
|
|
50
|
+
// stopped and deregistered the instant its refcount hits zero, so a deferred acquire can land on
|
|
51
|
+
// a dead instance — including a freshly-registered stream torn down during the init await below.
|
|
52
|
+
onAcquire?.(stream);
|
|
53
|
+
if (isNew) {
|
|
54
|
+
try {
|
|
55
|
+
await this.initStream(stream);
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
// Evict eagerly, before the error reaches the subscriber, so a synchronous resubscribe
|
|
59
|
+
// can't re-acquire this uninitialised instance.
|
|
60
|
+
streamRegister.removeStream(stream);
|
|
61
|
+
throw e;
|
|
62
|
+
}
|
|
63
|
+
// If a concurrent teardown deregistered this stream during init, init() has opened a receiver
|
|
64
|
+
// on an instance nothing can reach; close it.
|
|
65
|
+
if (streamRegister.getStream(identifierWithScope) !== stream) {
|
|
66
|
+
stream.stopStream();
|
|
67
|
+
}
|
|
49
68
|
}
|
|
50
69
|
return { stream, isNew };
|
|
51
70
|
}
|
|
@@ -61,19 +61,26 @@ class TelemetryService extends BaseStreamService_1.BaseStreamService {
|
|
|
61
61
|
callsigns: callsignsLookup?.lookupType === models_1.CallsignsLookupType.List ? callsignsLookup.lookupValue : [],
|
|
62
62
|
sources,
|
|
63
63
|
};
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
64
|
+
// `defer` acquires the stream only when subscribed; `finalize` releases it on unsubscribe, error
|
|
65
|
+
// and complete — so acquire and release stay balanced on every path.
|
|
66
|
+
return (0, rxjs_1.defer)(() => {
|
|
67
|
+
let acquired;
|
|
68
|
+
const stream$ = this.createStream(params.projectId, callsignsLookup, sources, scope, (stream) => {
|
|
69
|
+
acquired = stream;
|
|
70
|
+
stream.addSubscription(subscriptionParams);
|
|
71
|
+
}).then((stream) => {
|
|
72
|
+
if (!this.statusSubscription) {
|
|
73
|
+
this.statusSubscription = stream.statusStream$.subscribe((msg) => {
|
|
74
|
+
this.status = msg === models_1.SubscriberStatusEnum.STOPPED || msg === models_1.SubscriberStatusEnum.ALIVE;
|
|
75
|
+
this.status$.next(msg);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return (0, rxjs_1.from)(stream.messageStream$);
|
|
79
|
+
});
|
|
80
|
+
return (0, rxjs_1.from)(stream$).pipe((0, rxjs_1.mergeAll)(), (0, operators_1.filter)((message) => {
|
|
81
|
+
return callsigns.includes(message.callsign) && sources.includes(message.source);
|
|
82
|
+
}), (0, operators_1.finalize)(() => acquired?.removeSubscription(subscriptionParams, params.terminateReceiverGroupOnUnsubscribe)));
|
|
73
83
|
});
|
|
74
|
-
return (0, rxjs_1.from)(stream).pipe((0, rxjs_1.mergeAll)(), (0, operators_1.filter)((message) => {
|
|
75
|
-
return callsigns.includes(message.callsign) && sources.includes(message.source);
|
|
76
|
-
}));
|
|
77
84
|
}
|
|
78
85
|
/**
|
|
79
86
|
* A method to keep track of the current subscriptions
|
|
@@ -152,7 +159,7 @@ class TelemetryService extends BaseStreamService_1.BaseStreamService {
|
|
|
152
159
|
return `${params.projectId}-default-${(0, flattenCallsignsLookup_1.flattenCallsignsLookup)(callsignsLookup).join()}`;
|
|
153
160
|
return `${params.projectId}-default`;
|
|
154
161
|
}
|
|
155
|
-
async createStream(projectId, callsignsLookup, sources, scope) {
|
|
162
|
+
async createStream(projectId, callsignsLookup, sources, scope, onAcquire) {
|
|
156
163
|
const newStream = await this.createStreamFromConfig(identifier_1.IDENTIFIER_NAME_TELEMETRY, {
|
|
157
164
|
url: this.config.url,
|
|
158
165
|
projectId,
|
|
@@ -165,7 +172,7 @@ class TelemetryService extends BaseStreamService_1.BaseStreamService {
|
|
|
165
172
|
insecure: this.config.insecure,
|
|
166
173
|
transport: this.config.transport,
|
|
167
174
|
fetch: this.config.fetch,
|
|
168
|
-
});
|
|
175
|
+
}, onAcquire);
|
|
169
176
|
if (!newStream.isNew) {
|
|
170
177
|
newStream.stream.statusStream$.subscribe((msg) => {
|
|
171
178
|
this.streamStatusSubject$.next({
|
|
@@ -16,8 +16,10 @@ export class StreamRegister {
|
|
|
16
16
|
}
|
|
17
17
|
addStream(stream) {
|
|
18
18
|
try {
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
// Stop any existing occupant of this key before overwriting it.
|
|
20
|
+
const existing = this.teleStreams.get(stream.identifier);
|
|
21
|
+
if (existing && existing !== stream) {
|
|
22
|
+
existing.stopStream();
|
|
21
23
|
}
|
|
22
24
|
this.teleStreams.set(stream.identifier, stream);
|
|
23
25
|
}
|
|
@@ -37,7 +39,11 @@ export class StreamRegister {
|
|
|
37
39
|
removeStream(stream) {
|
|
38
40
|
try {
|
|
39
41
|
stream.stopStream();
|
|
40
|
-
this
|
|
42
|
+
// Only clear the entry if this instance still owns the key: a replacement may already hold it,
|
|
43
|
+
// and evicting that would leave subscribers forking duplicate streams for the scope.
|
|
44
|
+
if (this.teleStreams.get(stream.identifier) === stream) {
|
|
45
|
+
this.teleStreams.delete(stream.identifier);
|
|
46
|
+
}
|
|
41
47
|
}
|
|
42
48
|
catch (e) {
|
|
43
49
|
this.logger.error(`Failed to remove stream from list. identifier: ${stream.identifier}`, e);
|
|
@@ -31,6 +31,18 @@ export declare enum GamepadButtonType {
|
|
|
31
31
|
}
|
|
32
32
|
/** Per-axis tuning keyed by GamepadButtonType. Absent entirely on configs saved before axis tuning existed. */
|
|
33
33
|
export type GamepadAxisValueMap = Partial<Record<GamepadButtonType, number>>;
|
|
34
|
+
/** Same shape as GamepadAxisValueMap, keyed by any input — a modifier is usually a bumper or trigger, not an axis. */
|
|
35
|
+
export type GamepadInputValueMap = Partial<Record<GamepadButtonType, number>>;
|
|
36
|
+
/**
|
|
37
|
+
* Engage point for an input with no GamepadConfig.modifierThresholds entry. Exported so editor and
|
|
38
|
+
* interpreter agree: a modifier held at 0.5 in one and 0.75 in the other works on screen, not on the robot.
|
|
39
|
+
*/
|
|
40
|
+
export declare const DEFAULT_MODIFIER_THRESHOLD = 0.5;
|
|
41
|
+
/**
|
|
42
|
+
* Release point as a fraction of the engage threshold. Releasing at the engage value flaps the layer at
|
|
43
|
+
* frame rate while a trigger rests on the threshold. Exported for the same reason as the threshold.
|
|
44
|
+
*/
|
|
45
|
+
export declare const MODIFIER_RELEASE_FRACTION = 0.7;
|
|
34
46
|
export declare enum GamepadTriggerType {
|
|
35
47
|
ONCE = "once",
|
|
36
48
|
CONTINUOUSLY = "continuously"
|
|
@@ -54,6 +66,15 @@ export interface GamepadAction {
|
|
|
54
66
|
triggerType: GamepadTriggerType;
|
|
55
67
|
/** Milliseconds between repeats. Only meaningful for `continuously`. */
|
|
56
68
|
frequency: number;
|
|
69
|
+
/**
|
|
70
|
+
* Inputs that must be held for this action to fire. A set: unordered, and absent or empty is the base
|
|
71
|
+
* layer, eligible whatever is held — which is what leaves pre-modifier configs unaffected.
|
|
72
|
+
*
|
|
73
|
+
* Matched on `type` alone, since `text` is a display label and the same input reaches us as "LB" from
|
|
74
|
+
* one author and "L1" from another. Must not contain the action's own `source`, which the triggering
|
|
75
|
+
* press would satisfy on its own.
|
|
76
|
+
*/
|
|
77
|
+
modifiers?: GamepadButtonRef[];
|
|
57
78
|
}
|
|
58
79
|
export interface GamepadVariable {
|
|
59
80
|
name: string;
|
|
@@ -80,6 +101,11 @@ export interface GamepadConfig {
|
|
|
80
101
|
variables: GamepadVariable[];
|
|
81
102
|
axisDeadzones?: GamepadAxisValueMap;
|
|
82
103
|
axisExpos?: GamepadAxisValueMap;
|
|
104
|
+
/**
|
|
105
|
+
* Engage point per modifier input. Profile-level rather than per-action so one trigger cannot engage at
|
|
106
|
+
* two values. Absent entries use DEFAULT_MODIFIER_THRESHOLD.
|
|
107
|
+
*/
|
|
108
|
+
modifierThresholds?: GamepadInputValueMap;
|
|
83
109
|
}
|
|
84
110
|
/** One stored gamepad config, scoped to a specific physical controller model (GamepadConfig.gamepad). */
|
|
85
111
|
export type GamepadConfigItem = ConfigGroupItem<GamepadConfig>;
|
|
@@ -30,6 +30,16 @@ export var GamepadButtonType;
|
|
|
30
30
|
GamepadButtonType[GamepadButtonType["SQUARE"] = 26] = "SQUARE";
|
|
31
31
|
GamepadButtonType[GamepadButtonType["TRIANGLE"] = 27] = "TRIANGLE";
|
|
32
32
|
})(GamepadButtonType || (GamepadButtonType = {}));
|
|
33
|
+
/**
|
|
34
|
+
* Engage point for an input with no GamepadConfig.modifierThresholds entry. Exported so editor and
|
|
35
|
+
* interpreter agree: a modifier held at 0.5 in one and 0.75 in the other works on screen, not on the robot.
|
|
36
|
+
*/
|
|
37
|
+
export const DEFAULT_MODIFIER_THRESHOLD = 0.5;
|
|
38
|
+
/**
|
|
39
|
+
* Release point as a fraction of the engage threshold. Releasing at the engage value flaps the layer at
|
|
40
|
+
* frame rate while a trigger rests on the threshold. Exported for the same reason as the threshold.
|
|
41
|
+
*/
|
|
42
|
+
export const MODIFIER_RELEASE_FRACTION = 0.7;
|
|
33
43
|
export var GamepadTriggerType;
|
|
34
44
|
(function (GamepadTriggerType) {
|
|
35
45
|
GamepadTriggerType["ONCE"] = "once";
|
|
@@ -10,7 +10,7 @@ export declare abstract class BaseStreamService<T extends IBaseStream, C extends
|
|
|
10
10
|
protected constructor(name: string, config: IRocosSDKConfig);
|
|
11
11
|
getStatus(): boolean;
|
|
12
12
|
protected initStream(stream: T): Promise<void>;
|
|
13
|
-
protected createStreamFromConfig(identifier: string, config: C): Promise<{
|
|
13
|
+
protected createStreamFromConfig(identifier: string, config: C, onAcquire?: (stream: T) => void): Promise<{
|
|
14
14
|
stream: T;
|
|
15
15
|
isNew: boolean;
|
|
16
16
|
}>;
|
|
@@ -29,7 +29,7 @@ export class BaseStreamService {
|
|
|
29
29
|
}
|
|
30
30
|
authService.startTokenRefreshChecker();
|
|
31
31
|
}
|
|
32
|
-
async createStreamFromConfig(identifier, config) {
|
|
32
|
+
async createStreamFromConfig(identifier, config, onAcquire) {
|
|
33
33
|
const identifierWithScope = StreamRegister.getIdentifier(identifier, config.scope);
|
|
34
34
|
const streamRegister = StreamRegister.getInstance();
|
|
35
35
|
let stream = streamRegister.getStream(identifierWithScope);
|
|
@@ -42,7 +42,26 @@ export class BaseStreamService {
|
|
|
42
42
|
this.status$.next(msg);
|
|
43
43
|
});
|
|
44
44
|
streamRegister.addStream(stream);
|
|
45
|
-
|
|
45
|
+
}
|
|
46
|
+
// Acquire synchronously with the lookup/registration above, before any await. A stream is
|
|
47
|
+
// stopped and deregistered the instant its refcount hits zero, so a deferred acquire can land on
|
|
48
|
+
// a dead instance — including a freshly-registered stream torn down during the init await below.
|
|
49
|
+
onAcquire?.(stream);
|
|
50
|
+
if (isNew) {
|
|
51
|
+
try {
|
|
52
|
+
await this.initStream(stream);
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
// Evict eagerly, before the error reaches the subscriber, so a synchronous resubscribe
|
|
56
|
+
// can't re-acquire this uninitialised instance.
|
|
57
|
+
streamRegister.removeStream(stream);
|
|
58
|
+
throw e;
|
|
59
|
+
}
|
|
60
|
+
// If a concurrent teardown deregistered this stream during init, init() has opened a receiver
|
|
61
|
+
// on an instance nothing can reach; close it.
|
|
62
|
+
if (streamRegister.getStream(identifierWithScope) !== stream) {
|
|
63
|
+
stream.stopStream();
|
|
64
|
+
}
|
|
46
65
|
}
|
|
47
66
|
return { stream, isNew };
|
|
48
67
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BehaviorSubject, catchError, combineLatest, distinctUntilChanged, from, interval, map, mergeAll, of, startWith, } from 'rxjs';
|
|
1
|
+
import { BehaviorSubject, catchError, combineLatest, defer, distinctUntilChanged, from, interval, map, mergeAll, of, startWith, } from 'rxjs';
|
|
2
2
|
import { CallsignStatus, CallsignsLookup, CallsignsLookupType, RocosError, SubscriberStatusEnum, TelemetryMonitorStatus, errorCodes, } from '../models';
|
|
3
3
|
import { filter, finalize } from 'rxjs/operators';
|
|
4
4
|
import { BaseStreamService } from './BaseStreamService';
|
|
@@ -58,19 +58,26 @@ export class TelemetryService extends BaseStreamService {
|
|
|
58
58
|
callsigns: callsignsLookup?.lookupType === CallsignsLookupType.List ? callsignsLookup.lookupValue : [],
|
|
59
59
|
sources,
|
|
60
60
|
};
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
61
|
+
// `defer` acquires the stream only when subscribed; `finalize` releases it on unsubscribe, error
|
|
62
|
+
// and complete — so acquire and release stay balanced on every path.
|
|
63
|
+
return defer(() => {
|
|
64
|
+
let acquired;
|
|
65
|
+
const stream$ = this.createStream(params.projectId, callsignsLookup, sources, scope, (stream) => {
|
|
66
|
+
acquired = stream;
|
|
67
|
+
stream.addSubscription(subscriptionParams);
|
|
68
|
+
}).then((stream) => {
|
|
69
|
+
if (!this.statusSubscription) {
|
|
70
|
+
this.statusSubscription = stream.statusStream$.subscribe((msg) => {
|
|
71
|
+
this.status = msg === SubscriberStatusEnum.STOPPED || msg === SubscriberStatusEnum.ALIVE;
|
|
72
|
+
this.status$.next(msg);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return from(stream.messageStream$);
|
|
76
|
+
});
|
|
77
|
+
return from(stream$).pipe(mergeAll(), filter((message) => {
|
|
78
|
+
return callsigns.includes(message.callsign) && sources.includes(message.source);
|
|
79
|
+
}), finalize(() => acquired?.removeSubscription(subscriptionParams, params.terminateReceiverGroupOnUnsubscribe)));
|
|
70
80
|
});
|
|
71
|
-
return from(stream).pipe(mergeAll(), filter((message) => {
|
|
72
|
-
return callsigns.includes(message.callsign) && sources.includes(message.source);
|
|
73
|
-
}));
|
|
74
81
|
}
|
|
75
82
|
/**
|
|
76
83
|
* A method to keep track of the current subscriptions
|
|
@@ -149,7 +156,7 @@ export class TelemetryService extends BaseStreamService {
|
|
|
149
156
|
return `${params.projectId}-default-${flattenCallsignsLookup(callsignsLookup).join()}`;
|
|
150
157
|
return `${params.projectId}-default`;
|
|
151
158
|
}
|
|
152
|
-
async createStream(projectId, callsignsLookup, sources, scope) {
|
|
159
|
+
async createStream(projectId, callsignsLookup, sources, scope, onAcquire) {
|
|
153
160
|
const newStream = await this.createStreamFromConfig(IDENTIFIER_NAME_TELEMETRY, {
|
|
154
161
|
url: this.config.url,
|
|
155
162
|
projectId,
|
|
@@ -162,7 +169,7 @@ export class TelemetryService extends BaseStreamService {
|
|
|
162
169
|
insecure: this.config.insecure,
|
|
163
170
|
transport: this.config.transport,
|
|
164
171
|
fetch: this.config.fetch,
|
|
165
|
-
});
|
|
172
|
+
}, onAcquire);
|
|
166
173
|
if (!newStream.isNew) {
|
|
167
174
|
newStream.stream.statusStream$.subscribe((msg) => {
|
|
168
175
|
this.streamStatusSubject$.next({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dronedeploy/rocos-js-sdk",
|
|
3
|
-
"version": "4.4.
|
|
3
|
+
"version": "4.4.4",
|
|
4
4
|
"description": "Javascript SDK for rocos",
|
|
5
5
|
"main": "cjs/index.js",
|
|
6
6
|
"module": "esm/index.js",
|
|
@@ -28,12 +28,19 @@
|
|
|
28
28
|
"@grpc/grpc-js": "^1.7.1",
|
|
29
29
|
"@protobuf-ts/grpcweb-transport": "^2.9.1",
|
|
30
30
|
"@protobuf-ts/runtime": "^2.9.1",
|
|
31
|
+
"@protobuf-ts/runtime-rpc": "^2.9.1",
|
|
31
32
|
"acorn": "^8.11.3",
|
|
32
33
|
"loglevel": "^1.7.1"
|
|
33
34
|
},
|
|
34
35
|
"peerDependencies": {
|
|
36
|
+
"@types/node": "*",
|
|
35
37
|
"rxjs": "^6.6.6 || ^7.0.0"
|
|
36
38
|
},
|
|
39
|
+
"peerDependenciesMeta": {
|
|
40
|
+
"@types/node": {
|
|
41
|
+
"optional": true
|
|
42
|
+
}
|
|
43
|
+
},
|
|
37
44
|
"repository": {},
|
|
38
45
|
"private": false
|
|
39
46
|
}
|