mobility-toolbox-js 1.6.4-beta.2 → 1.6.5
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/api/tralis/TralisAPI.js +5 -10
- package/api/tralis/WebSocketConnector.js +16 -2
- package/api/tralis/WebSocketConnector.test.js +115 -5
- package/common/mixins/TrackerLayerMixin.js +22 -0
- package/common/mixins/TralisLayerMixin.js +13 -10
- package/common/utils/timeUtils.js +0 -28
- package/common/utils/timeUtils.test.js +1 -14
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/mapbox/layers/TralisLayer.js +8 -4
- package/ol/layers/TralisLayer.js +14 -5
- package/ol/layers/TralisLayer.test.js +45 -0
- package/package.json +1 -1
package/api/tralis/TralisAPI.js
CHANGED
|
@@ -64,6 +64,9 @@ class TralisAPI {
|
|
|
64
64
|
|
|
65
65
|
/** @ignore */
|
|
66
66
|
this.prefix = options.prefix || '';
|
|
67
|
+
|
|
68
|
+
/** @ignore */
|
|
69
|
+
this.onOpen = this.onOpen.bind(this);
|
|
67
70
|
}
|
|
68
71
|
|
|
69
72
|
defineProperties(options) {
|
|
@@ -149,16 +152,8 @@ class TralisAPI {
|
|
|
149
152
|
|
|
150
153
|
open() {
|
|
151
154
|
this.close();
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
// Register BBOX and PROJECTION messages on open.
|
|
155
|
-
if (this.conn.connecting) {
|
|
156
|
-
this.conn.websocket.addEventListener('open', () => {
|
|
157
|
-
this.onOpen();
|
|
158
|
-
});
|
|
159
|
-
} else {
|
|
160
|
-
this.onOpen();
|
|
161
|
-
}
|
|
155
|
+
// Register BBOX and PROJECTION messages must be send before previous subscriptions.
|
|
156
|
+
this.conn.connect(this.url, this.onOpen);
|
|
162
157
|
|
|
163
158
|
// Register reconnection on close.
|
|
164
159
|
this.conn.websocket.onclose = () => {
|
|
@@ -95,9 +95,11 @@ class WebSocketConnector {
|
|
|
95
95
|
/**
|
|
96
96
|
* (Re)connect the websocket.
|
|
97
97
|
*
|
|
98
|
+
* @param {strin} url Websocket url.
|
|
99
|
+
* @param {function} onOpen Callback called when the websocket connection is opened and before subscriptions of previous subscriptions.
|
|
98
100
|
* @private
|
|
99
101
|
*/
|
|
100
|
-
connect(url) {
|
|
102
|
+
connect(url, onOpen = () => {}) {
|
|
101
103
|
if (this.websocket && !this.closed) {
|
|
102
104
|
this.websocket.close();
|
|
103
105
|
}
|
|
@@ -107,9 +109,11 @@ class WebSocketConnector {
|
|
|
107
109
|
|
|
108
110
|
if (!this.open) {
|
|
109
111
|
this.websocket.addEventListener('open', () => {
|
|
112
|
+
onOpen();
|
|
110
113
|
this.subscribePreviousSubscriptions();
|
|
111
114
|
});
|
|
112
115
|
} else {
|
|
116
|
+
onOpen();
|
|
113
117
|
this.subscribePreviousSubscriptions();
|
|
114
118
|
}
|
|
115
119
|
}
|
|
@@ -124,6 +128,7 @@ class WebSocketConnector {
|
|
|
124
128
|
this.websocket.onclose = null;
|
|
125
129
|
this.websocket.close();
|
|
126
130
|
this.websocket = null;
|
|
131
|
+
this.messagesOnOpen = [];
|
|
127
132
|
}
|
|
128
133
|
}
|
|
129
134
|
|
|
@@ -148,6 +153,9 @@ class WebSocketConnector {
|
|
|
148
153
|
this.messagesOnOpen = [];
|
|
149
154
|
send();
|
|
150
155
|
});
|
|
156
|
+
this.websocket.addEventListener('close', () => {
|
|
157
|
+
this.messagesOnOpen = [];
|
|
158
|
+
});
|
|
151
159
|
}
|
|
152
160
|
} else if (!this.messagesOnOpen.includes(message)) {
|
|
153
161
|
send();
|
|
@@ -260,7 +268,6 @@ class WebSocketConnector {
|
|
|
260
268
|
this.send(`GET ${reqStr}`);
|
|
261
269
|
this.send(`SUB ${reqStr}`);
|
|
262
270
|
}
|
|
263
|
-
|
|
264
271
|
this.subscribed[reqStr] = true;
|
|
265
272
|
}
|
|
266
273
|
}
|
|
@@ -307,6 +314,13 @@ class WebSocketConnector {
|
|
|
307
314
|
* After an auto reconnection we need to re-subscribe to the channels.
|
|
308
315
|
*/
|
|
309
316
|
subscribePreviousSubscriptions() {
|
|
317
|
+
// Before to subscribe previous subscriptions we make sure they
|
|
318
|
+
// are all defined as unsubscribed, because this code is asynchrone
|
|
319
|
+
// and a subscription could have been added in between.
|
|
320
|
+
Object.keys(this.subscribed).forEach((key) => {
|
|
321
|
+
this.subscribed[key] = false;
|
|
322
|
+
});
|
|
323
|
+
|
|
310
324
|
// Subscribe all previous subscriptions.
|
|
311
325
|
[...this.subscriptions].forEach((s) => {
|
|
312
326
|
this.subscribe(s.params, s.cb, s.errorCb, s.quiet);
|
|
@@ -28,6 +28,31 @@ describe('WebSocketConnector', () => {
|
|
|
28
28
|
});
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
+
describe('#close', () => {
|
|
32
|
+
test('should close the websocket and clear some property', async () => {
|
|
33
|
+
// eslint-disable-next-line no-unused-vars
|
|
34
|
+
const client = new Connector();
|
|
35
|
+
const subsc2 = {
|
|
36
|
+
params: 'foo',
|
|
37
|
+
cb: () => {},
|
|
38
|
+
errorCb: () => {},
|
|
39
|
+
quiet: false,
|
|
40
|
+
};
|
|
41
|
+
client.subscriptions = [subsc2];
|
|
42
|
+
client.messagesOnOpen = ['GET foo'];
|
|
43
|
+
client.connect(`ws://foo:1234`);
|
|
44
|
+
client.websocket.addEventListener = jest.fn();
|
|
45
|
+
client.websocket.removeEventListener = jest.fn();
|
|
46
|
+
client.websocket.close = jest.fn();
|
|
47
|
+
await server.connected;
|
|
48
|
+
expect(client.websocket).toBeDefined();
|
|
49
|
+
expect(client.messagesOnOpen).toEqual(['GET foo']);
|
|
50
|
+
client.close();
|
|
51
|
+
expect(client.messagesOnOpen).toEqual([]);
|
|
52
|
+
expect(client.websocket).toBeNull();
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
31
56
|
describe('#connect', () => {
|
|
32
57
|
test('create a new WebSocket.', async () => {
|
|
33
58
|
const client = new Connector();
|
|
@@ -50,23 +75,44 @@ describe('WebSocketConnector', () => {
|
|
|
50
75
|
expect(client.websocket.readyState).toBe(WebSocket.CONNECTING);
|
|
51
76
|
});
|
|
52
77
|
|
|
53
|
-
test('
|
|
78
|
+
test('call onOpen function', async () => {
|
|
79
|
+
const onOpen = jest.fn();
|
|
54
80
|
const client = new Connector();
|
|
55
81
|
client.subscribe = jest.fn();
|
|
56
|
-
client.connect(`ws://foo:1234
|
|
82
|
+
client.connect(`ws://foo:1234`, onOpen);
|
|
57
83
|
await server.connected;
|
|
58
84
|
expect(client.websocket.readyState).toBe(WebSocket.OPEN);
|
|
85
|
+
expect(onOpen).toHaveBeenCalledTimes(1);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('subscribe previous subscriptions on open (quiet or not)', async () => {
|
|
89
|
+
const client = new Connector();
|
|
90
|
+
client.subscribe = jest.fn();
|
|
91
|
+
client.send = jest.fn();
|
|
59
92
|
const subsc = {
|
|
60
93
|
params: 'foo',
|
|
61
94
|
cb: () => {},
|
|
62
95
|
errorCb: () => {},
|
|
63
96
|
quiet: true,
|
|
64
97
|
};
|
|
65
|
-
|
|
98
|
+
const subsc2 = {
|
|
99
|
+
params: 'foo',
|
|
100
|
+
cb: () => {},
|
|
101
|
+
errorCb: () => {},
|
|
102
|
+
quiet: false,
|
|
103
|
+
};
|
|
104
|
+
client.subscriptions = [subsc, subsc2];
|
|
105
|
+
|
|
106
|
+
client.connect(`ws://foo:1234`);
|
|
107
|
+
await server.connected;
|
|
108
|
+
expect(client.websocket.readyState).toBe(WebSocket.OPEN);
|
|
109
|
+
expect(client.subscribe).toHaveBeenCalledTimes(2);
|
|
110
|
+
client.subscribe.mockReset();
|
|
111
|
+
|
|
66
112
|
client.connect(`ws://foo:12345`);
|
|
67
113
|
await server2.connected;
|
|
68
114
|
expect(client.websocket.readyState).toBe(WebSocket.OPEN);
|
|
69
|
-
expect(client.subscribe).toHaveBeenCalledTimes(
|
|
115
|
+
expect(client.subscribe).toHaveBeenCalledTimes(2);
|
|
70
116
|
expect(client.subscribe).toHaveBeenCalledWith(
|
|
71
117
|
subsc.params,
|
|
72
118
|
subsc.cb,
|
|
@@ -74,6 +120,66 @@ describe('WebSocketConnector', () => {
|
|
|
74
120
|
subsc.quiet,
|
|
75
121
|
);
|
|
76
122
|
});
|
|
123
|
+
|
|
124
|
+
test('send GET and SUB for not quiet previous subscriptions', async () => {
|
|
125
|
+
const client = new Connector();
|
|
126
|
+
client.send = jest.fn();
|
|
127
|
+
const subsc = {
|
|
128
|
+
params: { channel: 'foo' },
|
|
129
|
+
cb: () => {},
|
|
130
|
+
errorCb: () => {},
|
|
131
|
+
quiet: false,
|
|
132
|
+
};
|
|
133
|
+
client.subscriptions = [subsc];
|
|
134
|
+
|
|
135
|
+
client.connect(`ws://foo:1234`);
|
|
136
|
+
client.websocket.addEventListener = jest.fn();
|
|
137
|
+
client.websocket.removeEventListener = jest.fn();
|
|
138
|
+
await server.connected;
|
|
139
|
+
expect(client.websocket.readyState).toBe(WebSocket.OPEN);
|
|
140
|
+
expect(client.send).toHaveBeenCalledTimes(2);
|
|
141
|
+
expect(client.send.mock.calls[0]).toEqual(['GET foo']);
|
|
142
|
+
expect(client.send.mock.calls[1]).toEqual(['SUB foo']);
|
|
143
|
+
client.send.mockReset();
|
|
144
|
+
|
|
145
|
+
client.connect(`ws://foo:12345`);
|
|
146
|
+
client.websocket.addEventListener = jest.fn();
|
|
147
|
+
client.websocket.removeEventListener = jest.fn();
|
|
148
|
+
await server2.connected;
|
|
149
|
+
expect(client.websocket.readyState).toBe(WebSocket.OPEN);
|
|
150
|
+
// not quiet subscriptions will send GET and SUB requests.
|
|
151
|
+
expect(client.send).toHaveBeenCalledTimes(2);
|
|
152
|
+
expect(client.send.mock.calls[0]).toEqual(['GET foo']);
|
|
153
|
+
expect(client.send.mock.calls[1]).toEqual(['SUB foo']);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test('doesn\t send GET and SUB for quiet previous subscriptions', async () => {
|
|
157
|
+
const client = new Connector();
|
|
158
|
+
client.send = jest.fn();
|
|
159
|
+
const subsc = {
|
|
160
|
+
params: { channel: 'foo' },
|
|
161
|
+
cb: () => {},
|
|
162
|
+
errorCb: () => {},
|
|
163
|
+
quiet: true,
|
|
164
|
+
};
|
|
165
|
+
client.subscriptions = [subsc];
|
|
166
|
+
|
|
167
|
+
client.connect(`ws://foo:1234`);
|
|
168
|
+
client.websocket.addEventListener = jest.fn();
|
|
169
|
+
client.websocket.removeEventListener = jest.fn();
|
|
170
|
+
await server.connected;
|
|
171
|
+
expect(client.websocket.readyState).toBe(WebSocket.OPEN);
|
|
172
|
+
expect(client.send).toHaveBeenCalledTimes(0);
|
|
173
|
+
client.send.mockReset();
|
|
174
|
+
|
|
175
|
+
client.connect(`ws://foo:12345`);
|
|
176
|
+
client.websocket.addEventListener = jest.fn();
|
|
177
|
+
client.websocket.removeEventListener = jest.fn();
|
|
178
|
+
await server2.connected;
|
|
179
|
+
expect(client.websocket.readyState).toBe(WebSocket.OPEN);
|
|
180
|
+
// not quiet subscriptions will send GET and SUB requests.
|
|
181
|
+
expect(client.send).toHaveBeenCalledTimes(0);
|
|
182
|
+
});
|
|
77
183
|
});
|
|
78
184
|
|
|
79
185
|
describe('#subscribe', () => {
|
|
@@ -199,7 +305,11 @@ describe('WebSocketConnector', () => {
|
|
|
199
305
|
client.subscribe(params2, cb2);
|
|
200
306
|
expect(client.subscriptions.length).toBe(3);
|
|
201
307
|
expect(client.websocket.removeEventListener).toBeCalledTimes(2);
|
|
202
|
-
expect(
|
|
308
|
+
expect(
|
|
309
|
+
client.websocket.addEventListener.mock.calls.filter(
|
|
310
|
+
(c) => c[0] === 'message',
|
|
311
|
+
).length,
|
|
312
|
+
).toBe(5);
|
|
203
313
|
|
|
204
314
|
client.unsubscribe('foo');
|
|
205
315
|
expect(client.subscriptions.length).toBe(1);
|
|
@@ -398,6 +398,28 @@ const TrackerLayerMixin = (Base) =>
|
|
|
398
398
|
// writable: true,
|
|
399
399
|
// },
|
|
400
400
|
});
|
|
401
|
+
|
|
402
|
+
// When we use the delay style we want to display delayed train on top by default
|
|
403
|
+
if (this.useDelayStyle && !this.sort) {
|
|
404
|
+
this.sort = (traj1, traj2) => {
|
|
405
|
+
if (traj1.delay === null && traj2.delay !== null) {
|
|
406
|
+
return 1;
|
|
407
|
+
}
|
|
408
|
+
if (traj2.delay === null && traj1.delay !== null) {
|
|
409
|
+
return -1;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// We put cancelled train inbetween green and yellow trains
|
|
413
|
+
// >=180000ms corresponds to yellow train
|
|
414
|
+
if (traj1.cancelled && !traj2.cancelled) {
|
|
415
|
+
return traj2.delay < 180000 ? -1 : 1;
|
|
416
|
+
}
|
|
417
|
+
if (traj2.cancelled && !traj1.cancelled) {
|
|
418
|
+
return traj1.delay < 180000 ? 1 : -1;
|
|
419
|
+
}
|
|
420
|
+
return traj2.delay - traj1.delay;
|
|
421
|
+
};
|
|
422
|
+
}
|
|
401
423
|
}
|
|
402
424
|
|
|
403
425
|
/**
|
|
@@ -105,7 +105,7 @@ const TralisLayerMixin = (TrackerLayer) =>
|
|
|
105
105
|
};
|
|
106
106
|
|
|
107
107
|
// This property will call api.setBbox on each movend event
|
|
108
|
-
this.isUpdateBboxOnMoveEnd = options.isUpdateBboxOnMoveEnd
|
|
108
|
+
this.isUpdateBboxOnMoveEnd = options.isUpdateBboxOnMoveEnd !== false;
|
|
109
109
|
|
|
110
110
|
// Bind callbacks
|
|
111
111
|
this.onTrajectoryMessage = this.onTrajectoryMessage.bind(this);
|
|
@@ -127,14 +127,18 @@ const TralisLayerMixin = (TrackerLayer) =>
|
|
|
127
127
|
this.onDeleteTrajectoryMessage,
|
|
128
128
|
this.isUpdateBboxOnMoveEnd,
|
|
129
129
|
);
|
|
130
|
-
|
|
130
|
+
|
|
131
|
+
if (this.isUpdateBboxOnMoveEnd) {
|
|
132
|
+
// Update the bbox on each move end
|
|
133
|
+
this.setBbox();
|
|
134
|
+
}
|
|
131
135
|
}
|
|
132
136
|
|
|
133
137
|
stop() {
|
|
134
138
|
super.stop();
|
|
135
|
-
this.api.close();
|
|
136
139
|
this.api.unsubscribeTrajectory(this.onTrajectoryMessage);
|
|
137
140
|
this.api.unsubscribeDeletedVehicles(this.onDeleteTrajectoryMessage);
|
|
141
|
+
this.api.close();
|
|
138
142
|
}
|
|
139
143
|
|
|
140
144
|
setBbox(extent, zoom) {
|
|
@@ -151,24 +155,23 @@ const TralisLayerMixin = (TrackerLayer) =>
|
|
|
151
155
|
}
|
|
152
156
|
}
|
|
153
157
|
|
|
154
|
-
|
|
155
|
-
/* @ignore */
|
|
156
|
-
this.generalizationLevel = this.generalizationLevelByZoom[zoom];
|
|
157
|
-
|
|
158
|
-
const bbox = extent;
|
|
158
|
+
const bbox = [...extent];
|
|
159
159
|
|
|
160
|
+
if (this.isUpdateBboxOnMoveEnd) {
|
|
160
161
|
bbox.push(zoom);
|
|
161
162
|
|
|
162
163
|
if (this.tenant) {
|
|
163
164
|
bbox.push(`tenant=${this.tenant}`);
|
|
164
165
|
}
|
|
165
166
|
|
|
167
|
+
/* @ignore */
|
|
168
|
+
this.generalizationLevel = this.generalizationLevelByZoom[zoom];
|
|
166
169
|
if (this.generalizationLevel) {
|
|
167
170
|
bbox.push(`gen=${this.generalizationLevel}`);
|
|
168
171
|
}
|
|
169
|
-
|
|
170
|
-
this.api.bbox = bbox;
|
|
171
172
|
}
|
|
173
|
+
|
|
174
|
+
this.api.bbox = bbox;
|
|
172
175
|
}
|
|
173
176
|
|
|
174
177
|
setMode(mode) {
|
|
@@ -42,31 +42,3 @@ export const getHoursAndMinutes = (timeInMs) => {
|
|
|
42
42
|
const date = new Date(timeInMs);
|
|
43
43
|
return `${pad(date.getHours())}:${pad(date.getMinutes())}`;
|
|
44
44
|
};
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Returns a string representing a delay.
|
|
48
|
-
* @param {Number} timeInMs Delay time in milliseconds.
|
|
49
|
-
* @ignore
|
|
50
|
-
*/
|
|
51
|
-
export const getDelayString = (timeInMs) => {
|
|
52
|
-
const h = Math.floor(timeInMs / 3600000);
|
|
53
|
-
const m = Math.floor((timeInMs % 3600000) / 60000);
|
|
54
|
-
const s = Math.floor(((timeInMs % 3600000) % 60000) / 1000);
|
|
55
|
-
|
|
56
|
-
if (s === 0 && h === 0 && m === 0) {
|
|
57
|
-
return '0';
|
|
58
|
-
}
|
|
59
|
-
if (s === 0 && h === 0) {
|
|
60
|
-
return `${m}m`;
|
|
61
|
-
}
|
|
62
|
-
if (s === 0) {
|
|
63
|
-
return `${h}h${m}m`;
|
|
64
|
-
}
|
|
65
|
-
if (m === 0 && h === 0) {
|
|
66
|
-
return `${s}s`;
|
|
67
|
-
}
|
|
68
|
-
if (h === 0) {
|
|
69
|
-
return `${m}m${s}s`;
|
|
70
|
-
}
|
|
71
|
-
return `${h}h${m}m${s}s`;
|
|
72
|
-
};
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getUTCDateString,
|
|
3
|
-
getUTCTimeString,
|
|
4
|
-
getHoursAndMinutes,
|
|
5
|
-
getDelayString,
|
|
6
|
-
} from './timeUtils';
|
|
1
|
+
import { getUTCDateString, getUTCTimeString } from './timeUtils';
|
|
7
2
|
|
|
8
3
|
describe('timeUtils', () => {
|
|
9
4
|
test('getUTCDateString should be correct.', () => {
|
|
@@ -18,12 +13,4 @@ describe('timeUtils', () => {
|
|
|
18
13
|
'11:5:1.123',
|
|
19
14
|
);
|
|
20
15
|
});
|
|
21
|
-
|
|
22
|
-
test('getHoursAndMinutes should be correct.', () => {
|
|
23
|
-
expect(getHoursAndMinutes(123456)).toBe('00:02');
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
test('getDelayString should be correct.', () => {
|
|
27
|
-
expect(getDelayString(123456)).toBe('2m3s');
|
|
28
|
-
});
|
|
29
16
|
});
|