brilliantsole 0.0.1
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/LICENSE +21 -0
- package/README.md +21 -0
- package/build/brilliantsole.cjs +5957 -0
- package/build/brilliantsole.cjs.map +1 -0
- package/build/brilliantsole.js +5448 -0
- package/build/brilliantsole.js.map +1 -0
- package/build/brilliantsole.ls.js +4872 -0
- package/build/brilliantsole.ls.js.map +1 -0
- package/build/brilliantsole.min.js +6 -0
- package/build/brilliantsole.min.js.map +1 -0
- package/build/brilliantsole.module.d.ts +908 -0
- package/build/brilliantsole.module.js +5412 -0
- package/build/brilliantsole.module.js.map +1 -0
- package/build/brilliantsole.module.min.d.ts +908 -0
- package/build/brilliantsole.module.min.js +6 -0
- package/build/brilliantsole.module.min.js.map +1 -0
- package/build/brilliantsole.node.module.d.ts +908 -0
- package/build/brilliantsole.node.module.js +5906 -0
- package/build/brilliantsole.node.module.js.map +1 -0
- package/build/dts/BS.d.ts +25 -0
- package/build/dts/Device.d.ts +136 -0
- package/build/dts/DeviceInformationManager.d.ts +56 -0
- package/build/dts/DeviceManager.d.ts +67 -0
- package/build/dts/FileTransferManager.d.ts +84 -0
- package/build/dts/FirmwareManager.d.ts +71 -0
- package/build/dts/InformationManager.d.ts +66 -0
- package/build/dts/TfliteManager.d.ts +92 -0
- package/build/dts/connection/BaseConnectionManager.d.ts +59 -0
- package/build/dts/connection/ClientConnectionManager.d.ts +23 -0
- package/build/dts/connection/WebSocketClientConnectionManager.d.ts +23 -0
- package/build/dts/connection/bluetooth/BluetoothConnectionManager.d.ts +10 -0
- package/build/dts/connection/bluetooth/NobleConnectionManager.d.ts +42 -0
- package/build/dts/connection/bluetooth/WebBluetoothConnectionManager.d.ts +20 -0
- package/build/dts/connection/bluetooth/bluetoothUUIDs.d.ts +14 -0
- package/build/dts/connection/webSocket/ClientConnectionManager.d.ts +23 -0
- package/build/dts/connection/webSocket/WebSocketClientConnectionManager.d.ts +23 -0
- package/build/dts/devicePair/DevicePair.d.ts +60 -0
- package/build/dts/devicePair/DevicePairPressureSensorDataManager.d.ts +25 -0
- package/build/dts/devicePair/DevicePairSensorDataManager.d.ts +33 -0
- package/build/dts/scanner/BaseScanner.d.ts +66 -0
- package/build/dts/scanner/NobleScanner.d.ts +17 -0
- package/build/dts/scanner/Scanner.d.ts +3 -0
- package/build/dts/sensor/BarometerSensorDataManager.d.ts +16 -0
- package/build/dts/sensor/MotionSensorDataManager.d.ts +69 -0
- package/build/dts/sensor/PressureSensorDataManager.d.ts +36 -0
- package/build/dts/sensor/SensorConfigurationManager.d.ts +44 -0
- package/build/dts/sensor/SensorDataManager.d.ts +40 -0
- package/build/dts/server/BaseClient.d.ts +85 -0
- package/build/dts/server/BaseServer.d.ts +48 -0
- package/build/dts/server/ServerUtils.d.ts +23 -0
- package/build/dts/server/udp/UDPServer.d.ts +11 -0
- package/build/dts/server/udp/UDPUtils.d.ts +9 -0
- package/build/dts/server/websocket/WebSocketClient.d.ts +17 -0
- package/build/dts/server/websocket/WebSocketServer.d.ts +13 -0
- package/build/dts/server/websocket/WebSocketUtils.d.ts +9 -0
- package/build/dts/utils/ArrayBufferUtils.d.ts +7 -0
- package/build/dts/utils/ArrayUtils.d.ts +2 -0
- package/build/dts/utils/CenterOfPressureHelper.d.ts +15 -0
- package/build/dts/utils/Console.d.ts +34 -0
- package/build/dts/utils/EventDispatcher.d.ts +50 -0
- package/build/dts/utils/EventUtils.d.ts +6 -0
- package/build/dts/utils/MathUtils.d.ts +21 -0
- package/build/dts/utils/ParseUtils.d.ts +5 -0
- package/build/dts/utils/RangeHelper.d.ts +8 -0
- package/build/dts/utils/Text.d.ts +6 -0
- package/build/dts/utils/Timer.d.ts +14 -0
- package/build/dts/utils/TypeScriptUtils.d.ts +19 -0
- package/build/dts/utils/cbor.d.ts +6 -0
- package/build/dts/utils/checksum.d.ts +3 -0
- package/build/dts/utils/environment.d.ts +13 -0
- package/build/dts/utils/mcumgr.d.ts +88 -0
- package/build/dts/utils/stringUtils.d.ts +2 -0
- package/build/dts/vibration/VibrationManager.d.ts +45 -0
- package/build/dts/vibration/VibrationWaveformEffects.d.ts +2 -0
- package/build/index.d.ts +908 -0
- package/build/index.node.d.ts +908 -0
- package/examples/3d/index.html +109 -0
- package/examples/3d/scene.html +57 -0
- package/examples/3d/script.js +419 -0
- package/examples/balance/index.html +138 -0
- package/examples/balance/script.js +243 -0
- package/examples/basic/index.html +327 -0
- package/examples/basic/script.js +1093 -0
- package/examples/center-of-pressure/index.html +132 -0
- package/examples/center-of-pressure/script.js +207 -0
- package/examples/device-pair/index.html +72 -0
- package/examples/device-pair/script.js +187 -0
- package/examples/edge-impulse/index.html +94 -0
- package/examples/edge-impulse/script.js +1033 -0
- package/examples/graph/index.html +83 -0
- package/examples/graph/script.js +469 -0
- package/examples/machine-learning/index.html +366 -0
- package/examples/machine-learning/script.js +1774 -0
- package/examples/pressure/index.html +145 -0
- package/examples/pressure/script.js +201 -0
- package/examples/recording/index.html +187 -0
- package/examples/recording/script.js +736 -0
- package/examples/server/index.html +266 -0
- package/examples/server/script.js +925 -0
- package/examples/utils/aframe/fingertip-button-component.js +201 -0
- package/examples/utils/aframe/fingertip-collider-target-component.js +102 -0
- package/examples/utils/aframe/fingertip-colliders-component.js +147 -0
- package/examples/utils/three/three.module.min.js +24846 -0
- package/examples/webxr/index.html +221 -0
- package/examples/webxr/script.js +1127 -0
- package/package.json +83 -0
- package/src/BS.ts +68 -0
- package/src/Device.ts +734 -0
- package/src/DeviceInformationManager.ts +146 -0
- package/src/DeviceManager.ts +354 -0
- package/src/FileTransferManager.ts +452 -0
- package/src/FirmwareManager.ts +357 -0
- package/src/InformationManager.ts +283 -0
- package/src/TfliteManager.ts +450 -0
- package/src/connection/BaseConnectionManager.ts +255 -0
- package/src/connection/ClientConnectionManager.ts +120 -0
- package/src/connection/bluetooth/BluetoothConnectionManager.ts +34 -0
- package/src/connection/bluetooth/NobleConnectionManager.ts +302 -0
- package/src/connection/bluetooth/WebBluetoothConnectionManager.ts +269 -0
- package/src/connection/bluetooth/bluetoothUUIDs.ts +218 -0
- package/src/devicePair/DevicePair.ts +253 -0
- package/src/devicePair/DevicePairPressureSensorDataManager.ts +82 -0
- package/src/devicePair/DevicePairSensorDataManager.ts +90 -0
- package/src/scanner/BaseScanner.ts +189 -0
- package/src/scanner/NobleScanner.ts +195 -0
- package/src/scanner/Scanner.ts +16 -0
- package/src/sensor/BarometerSensorDataManager.ts +41 -0
- package/src/sensor/MotionSensorDataManager.ts +151 -0
- package/src/sensor/PressureSensorDataManager.ts +112 -0
- package/src/sensor/SensorConfigurationManager.ts +177 -0
- package/src/sensor/SensorDataManager.ts +166 -0
- package/src/server/BaseClient.ts +368 -0
- package/src/server/BaseServer.ts +344 -0
- package/src/server/ServerUtils.ts +93 -0
- package/src/server/udp/UDPServer.ts +229 -0
- package/src/server/udp/UDPUtils.ts +20 -0
- package/src/server/websocket/WebSocketClient.ts +179 -0
- package/src/server/websocket/WebSocketServer.ts +184 -0
- package/src/server/websocket/WebSocketUtils.ts +20 -0
- package/src/utils/ArrayBufferUtils.ts +88 -0
- package/src/utils/ArrayUtils.ts +15 -0
- package/src/utils/CenterOfPressureHelper.ts +39 -0
- package/src/utils/Console.ts +156 -0
- package/src/utils/EventDispatcher.ts +153 -0
- package/src/utils/EventUtils.ts +41 -0
- package/src/utils/MathUtils.ts +53 -0
- package/src/utils/ParseUtils.ts +46 -0
- package/src/utils/RangeHelper.ts +38 -0
- package/src/utils/Text.ts +30 -0
- package/src/utils/Timer.ts +72 -0
- package/src/utils/TypeScriptUtils.ts +22 -0
- package/src/utils/cbor.js +429 -0
- package/src/utils/checksum.ts +41 -0
- package/src/utils/environment.ts +46 -0
- package/src/utils/mcumgr.js +444 -0
- package/src/utils/stringUtils.ts +11 -0
- package/src/vibration/VibrationManager.ts +308 -0
- package/src/vibration/VibrationWaveformEffects.ts +128 -0
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { createConsole } from "../utils/Console.ts";
|
|
2
|
+
import EventDispatcher, { BoundEventListeners, Event, EventMap } from "../utils/EventDispatcher.ts";
|
|
3
|
+
import {
|
|
4
|
+
createServerMessage,
|
|
5
|
+
ServerMessageTypes,
|
|
6
|
+
DeviceMessage,
|
|
7
|
+
ServerMessage,
|
|
8
|
+
ServerMessageType,
|
|
9
|
+
createDeviceMessage,
|
|
10
|
+
} from "./ServerUtils.ts";
|
|
11
|
+
import Device, { BoundDeviceEventListeners, DeviceEventMap, DeviceEventType } from "../Device.ts";
|
|
12
|
+
import { addEventListeners, removeEventListeners } from "../utils/EventUtils.ts";
|
|
13
|
+
import scanner from "../scanner/Scanner.ts";
|
|
14
|
+
import { parseMessage, parseStringFromDataView } from "../utils/ParseUtils.ts";
|
|
15
|
+
import { ConnectionMessageType, ConnectionMessageTypes } from "../connection/BaseConnectionManager.ts";
|
|
16
|
+
import { BoundScannerEventListeners, DiscoveredDevice, ScannerEventMap } from "../scanner/BaseScanner.ts";
|
|
17
|
+
import { concatenateArrayBuffers } from "../utils/ArrayBufferUtils.ts";
|
|
18
|
+
import DeviceManager, { DeviceManagerEventMap, BoundDeviceManagerEventListeners } from "../DeviceManager.ts";
|
|
19
|
+
|
|
20
|
+
const _console = createConsole("BaseServer", { log: true });
|
|
21
|
+
|
|
22
|
+
export const ServerEventTypes = ["clientConnected", "clientDisconnected"] as const;
|
|
23
|
+
export type ServerEventType = (typeof ServerEventTypes)[number];
|
|
24
|
+
|
|
25
|
+
interface ServerEventMessages {
|
|
26
|
+
clientConnected: { client: any };
|
|
27
|
+
clientDisconnected: { client: any };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type ServerEventDispatcher = EventDispatcher<BaseServer, ServerEventType, ServerEventMessages>;
|
|
31
|
+
export type ServerEvent = Event<BaseServer, ServerEventType, ServerEventMessages>;
|
|
32
|
+
export type ServerEventMap = EventMap<BaseServer, ServerEventType, ServerEventMessages>;
|
|
33
|
+
export type BoundServerEventListeners = BoundEventListeners<BaseServer, ServerEventType, ServerEventMessages>;
|
|
34
|
+
|
|
35
|
+
abstract class BaseServer {
|
|
36
|
+
// EVENT DISPATCHER
|
|
37
|
+
protected eventDispatcher: ServerEventDispatcher = new EventDispatcher(this as BaseServer, ServerEventTypes);
|
|
38
|
+
get addEventListener() {
|
|
39
|
+
return this.eventDispatcher.addEventListener;
|
|
40
|
+
}
|
|
41
|
+
protected get dispatchEvent() {
|
|
42
|
+
return this.eventDispatcher.dispatchEvent;
|
|
43
|
+
}
|
|
44
|
+
get removeEventListener() {
|
|
45
|
+
return this.eventDispatcher.removeEventListener;
|
|
46
|
+
}
|
|
47
|
+
get waitForEvent() {
|
|
48
|
+
return this.eventDispatcher.waitForEvent;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// CONSTRUCTOR
|
|
52
|
+
|
|
53
|
+
constructor() {
|
|
54
|
+
_console.assertWithError(scanner, "no scanner defined");
|
|
55
|
+
|
|
56
|
+
addEventListeners(scanner, this.#boundScannerListeners);
|
|
57
|
+
addEventListeners(DeviceManager, this.#boundDeviceManagerListeners);
|
|
58
|
+
addEventListeners(this, this.#boundServerListeners);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
get numberOfClients() {
|
|
62
|
+
return 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static #ClearSensorConfigurationsWhenNoClients = true;
|
|
66
|
+
static get ClearSensorConfigurationsWhenNoClients() {
|
|
67
|
+
return this.#ClearSensorConfigurationsWhenNoClients;
|
|
68
|
+
}
|
|
69
|
+
static set ClearSensorConfigurationsWhenNoClients(newValue) {
|
|
70
|
+
_console.assertTypeWithError(newValue, "boolean");
|
|
71
|
+
this.#ClearSensorConfigurationsWhenNoClients = newValue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
#clearSensorConfigurationsWhenNoClients = BaseServer.#ClearSensorConfigurationsWhenNoClients;
|
|
75
|
+
get clearSensorConfigurationsWhenNoClients() {
|
|
76
|
+
return this.#clearSensorConfigurationsWhenNoClients;
|
|
77
|
+
}
|
|
78
|
+
set clearSensorConfigurationsWhenNoClients(newValue) {
|
|
79
|
+
_console.assertTypeWithError(newValue, "boolean");
|
|
80
|
+
this.#clearSensorConfigurationsWhenNoClients = newValue;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// SERVER LISTENERS
|
|
84
|
+
#boundServerListeners: BoundServerEventListeners = {
|
|
85
|
+
clientConnected: this.#onClientConnected.bind(this),
|
|
86
|
+
clientDisconnected: this.#onClientDisconnected.bind(this),
|
|
87
|
+
};
|
|
88
|
+
#onClientConnected(event: ServerEventMap["clientConnected"]) {
|
|
89
|
+
const client = event.message.client;
|
|
90
|
+
_console.log("onClientConnected");
|
|
91
|
+
}
|
|
92
|
+
#onClientDisconnected(event: ServerEventMap["clientDisconnected"]) {
|
|
93
|
+
const client = event.message.client;
|
|
94
|
+
_console.log("onClientDisconnected");
|
|
95
|
+
if (this.numberOfClients == 0 && this.clearSensorConfigurationsWhenNoClients) {
|
|
96
|
+
DeviceManager.ConnectedDevices.forEach((device) => {
|
|
97
|
+
device.clearSensorConfiguration();
|
|
98
|
+
device.setTfliteInferencingEnabled(false);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// CLIENT MESSAGING
|
|
104
|
+
broadcastMessage(message: ArrayBuffer) {
|
|
105
|
+
_console.log("broadcasting", message);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// SCANNER
|
|
109
|
+
#boundScannerListeners: BoundScannerEventListeners = {
|
|
110
|
+
isScanningAvailable: this.#onScannerIsAvailable.bind(this),
|
|
111
|
+
isScanning: this.#onScannerIsScanning.bind(this),
|
|
112
|
+
discoveredDevice: this.#onScannerDiscoveredDevice.bind(this),
|
|
113
|
+
expiredDiscoveredDevice: this.#onExpiredDiscoveredDevice.bind(this),
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
#onScannerIsAvailable(event: ScannerEventMap["isScanningAvailable"]) {
|
|
117
|
+
this.broadcastMessage(this.#isScanningAvailableMessage);
|
|
118
|
+
}
|
|
119
|
+
get #isScanningAvailableMessage() {
|
|
120
|
+
return createServerMessage({ type: "isScanningAvailable", data: scanner!.isScanningAvailable });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
#onScannerIsScanning(event: ScannerEventMap["isScanning"]) {
|
|
124
|
+
this.broadcastMessage(this.#isScanningMessage);
|
|
125
|
+
}
|
|
126
|
+
get #isScanningMessage() {
|
|
127
|
+
return createServerMessage({ type: "isScanning", data: scanner!.isScanning });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
#onScannerDiscoveredDevice(event: ScannerEventMap["discoveredDevice"]) {
|
|
131
|
+
const { discoveredDevice } = event.message;
|
|
132
|
+
_console.log(discoveredDevice);
|
|
133
|
+
|
|
134
|
+
this.broadcastMessage(this.#createDiscoveredDeviceMessage(discoveredDevice));
|
|
135
|
+
}
|
|
136
|
+
#createDiscoveredDeviceMessage(discoveredDevice: DiscoveredDevice) {
|
|
137
|
+
return createServerMessage({ type: "discoveredDevice", data: discoveredDevice });
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
#onExpiredDiscoveredDevice(event: ScannerEventMap["expiredDiscoveredDevice"]) {
|
|
141
|
+
const { discoveredDevice } = event.message;
|
|
142
|
+
_console.log("expired", discoveredDevice);
|
|
143
|
+
this.broadcastMessage(this.#createExpiredDiscoveredDeviceMessage(discoveredDevice));
|
|
144
|
+
}
|
|
145
|
+
#createExpiredDiscoveredDeviceMessage(discoveredDevice: DiscoveredDevice) {
|
|
146
|
+
return createServerMessage({ type: "expiredDiscoveredDevice", data: discoveredDevice.bluetoothId });
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
get #discoveredDevicesMessage() {
|
|
150
|
+
const serverMessages: ServerMessage[] = scanner!.discoveredDevicesArray
|
|
151
|
+
.filter((discoveredDevice) => {
|
|
152
|
+
const existingConnectedDevice = DeviceManager.ConnectedDevices.find(
|
|
153
|
+
(device) => device.bluetoothId == discoveredDevice.bluetoothId
|
|
154
|
+
);
|
|
155
|
+
return !existingConnectedDevice;
|
|
156
|
+
})
|
|
157
|
+
.map((discoveredDevice) => {
|
|
158
|
+
return { type: "discoveredDevice", data: discoveredDevice };
|
|
159
|
+
});
|
|
160
|
+
return createServerMessage(...serverMessages);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
get #connectedDevicesMessage() {
|
|
164
|
+
return createServerMessage({
|
|
165
|
+
type: "connectedDevices",
|
|
166
|
+
data: JSON.stringify(DeviceManager.ConnectedDevices.map((device) => device.bluetoothId)),
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// DEVICE LISTENERS
|
|
171
|
+
|
|
172
|
+
#boundDeviceListeners: BoundDeviceEventListeners = {
|
|
173
|
+
connectionMessage: this.#onDeviceConnectionMessage.bind(this),
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
#createDeviceMessage(device: Device, messageType: ConnectionMessageType, dataView?: DataView): DeviceMessage {
|
|
177
|
+
return {
|
|
178
|
+
type: messageType as DeviceEventType,
|
|
179
|
+
data: dataView || device.latestConnectionMessage.get(messageType),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
#onDeviceConnectionMessage(deviceEvent: DeviceEventMap["connectionMessage"]) {
|
|
184
|
+
const { target: device, message } = deviceEvent;
|
|
185
|
+
_console.log("onDeviceConnectionMessage", deviceEvent.message);
|
|
186
|
+
|
|
187
|
+
if (!device.isConnected) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const { messageType, dataView } = message;
|
|
192
|
+
|
|
193
|
+
this.broadcastMessage(
|
|
194
|
+
this.#createDeviceServerMessage(device, this.#createDeviceMessage(device, messageType, dataView))
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// STATIC DEVICE LISTENERS
|
|
199
|
+
#boundDeviceManagerListeners: BoundDeviceManagerEventListeners = {
|
|
200
|
+
deviceConnected: this.#onDeviceConnected.bind(this),
|
|
201
|
+
deviceDisconnected: this.#onDeviceDisconnected.bind(this),
|
|
202
|
+
deviceIsConnected: this.#onDeviceIsConnected.bind(this),
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
#onDeviceConnected(staticDeviceEvent: DeviceManagerEventMap["deviceConnected"]) {
|
|
206
|
+
const { device } = staticDeviceEvent.message;
|
|
207
|
+
_console.log("onDeviceConnected", device.bluetoothId);
|
|
208
|
+
addEventListeners(device, this.#boundDeviceListeners);
|
|
209
|
+
device.isServerSide = true;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
#onDeviceDisconnected(staticDeviceEvent: DeviceManagerEventMap["deviceDisconnected"]) {
|
|
213
|
+
const { device } = staticDeviceEvent.message;
|
|
214
|
+
_console.log("onDeviceDisconnected", device.bluetoothId);
|
|
215
|
+
removeEventListeners(device, this.#boundDeviceListeners);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
#onDeviceIsConnected(staticDeviceEvent: DeviceManagerEventMap["deviceIsConnected"]) {
|
|
219
|
+
const { device } = staticDeviceEvent.message;
|
|
220
|
+
_console.log("onDeviceIsConnected", device.bluetoothId);
|
|
221
|
+
this.broadcastMessage(this.#createDeviceIsConnectedMessage(device));
|
|
222
|
+
}
|
|
223
|
+
#createDeviceIsConnectedMessage(device: Device) {
|
|
224
|
+
return this.#createDeviceServerMessage(device, { type: "isConnected", data: device.isConnected });
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
#createDeviceServerMessage(device: Device, ...messages: DeviceMessage[]) {
|
|
228
|
+
return createServerMessage({
|
|
229
|
+
type: "deviceMessage",
|
|
230
|
+
data: [device.bluetoothId!, createDeviceMessage(...messages)],
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// PARSING
|
|
235
|
+
protected parseClientMessage(dataView: DataView) {
|
|
236
|
+
let responseMessages: ArrayBuffer[] = [];
|
|
237
|
+
|
|
238
|
+
parseMessage(dataView, ServerMessageTypes, this.#onClientMessage.bind(this), { responseMessages }, true);
|
|
239
|
+
|
|
240
|
+
responseMessages = responseMessages.filter(Boolean);
|
|
241
|
+
|
|
242
|
+
if (responseMessages.length > 0) {
|
|
243
|
+
return concatenateArrayBuffers(responseMessages);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
#onClientMessage(messageType: ServerMessageType, dataView: DataView, context: { responseMessages: ArrayBuffer[] }) {
|
|
248
|
+
const { responseMessages } = context;
|
|
249
|
+
switch (messageType) {
|
|
250
|
+
case "isScanningAvailable":
|
|
251
|
+
responseMessages.push(this.#isScanningAvailableMessage);
|
|
252
|
+
break;
|
|
253
|
+
case "isScanning":
|
|
254
|
+
responseMessages.push(this.#isScanningMessage);
|
|
255
|
+
break;
|
|
256
|
+
case "startScan":
|
|
257
|
+
scanner!.startScan();
|
|
258
|
+
break;
|
|
259
|
+
case "stopScan":
|
|
260
|
+
scanner!.stopScan();
|
|
261
|
+
break;
|
|
262
|
+
case "discoveredDevices":
|
|
263
|
+
responseMessages.push(this.#discoveredDevicesMessage);
|
|
264
|
+
break;
|
|
265
|
+
case "connectToDevice":
|
|
266
|
+
{
|
|
267
|
+
const { string: deviceId } = parseStringFromDataView(dataView);
|
|
268
|
+
scanner!.connectToDevice(deviceId);
|
|
269
|
+
}
|
|
270
|
+
break;
|
|
271
|
+
case "disconnectFromDevice":
|
|
272
|
+
{
|
|
273
|
+
const { string: deviceId } = parseStringFromDataView(dataView);
|
|
274
|
+
const device = DeviceManager.ConnectedDevices.find((device) => device.bluetoothId == deviceId);
|
|
275
|
+
if (!device) {
|
|
276
|
+
_console.error(`no device found with id ${deviceId}`);
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
device.disconnect();
|
|
280
|
+
}
|
|
281
|
+
break;
|
|
282
|
+
case "connectedDevices":
|
|
283
|
+
responseMessages.push(this.#connectedDevicesMessage);
|
|
284
|
+
break;
|
|
285
|
+
case "deviceMessage":
|
|
286
|
+
{
|
|
287
|
+
const { string: deviceId, byteOffset } = parseStringFromDataView(dataView);
|
|
288
|
+
const device = DeviceManager.ConnectedDevices.find((device) => device.bluetoothId == deviceId);
|
|
289
|
+
if (!device) {
|
|
290
|
+
_console.error(`no device found with id ${deviceId}`);
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
const _dataView = new DataView(dataView.buffer, dataView.byteOffset + byteOffset);
|
|
294
|
+
const responseMessage = this.parseClientDeviceMessage(device, _dataView);
|
|
295
|
+
if (responseMessage) {
|
|
296
|
+
responseMessages.push(responseMessage);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
break;
|
|
300
|
+
default:
|
|
301
|
+
_console.error(`uncaught messageType "${messageType}"`);
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
protected parseClientDeviceMessage(device: Device, dataView: DataView) {
|
|
307
|
+
_console.log("onDeviceMessage", device.bluetoothId, dataView);
|
|
308
|
+
|
|
309
|
+
let responseMessages: DeviceMessage[] = [];
|
|
310
|
+
|
|
311
|
+
parseMessage(
|
|
312
|
+
dataView,
|
|
313
|
+
ConnectionMessageTypes,
|
|
314
|
+
this.#parseClientDeviceMessageCallback.bind(this),
|
|
315
|
+
{ responseMessages, device },
|
|
316
|
+
true
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
if (responseMessages.length > 0) {
|
|
320
|
+
return this.#createDeviceServerMessage(device, ...responseMessages);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
#parseClientDeviceMessageCallback(
|
|
325
|
+
messageType: ConnectionMessageType,
|
|
326
|
+
dataView: DataView,
|
|
327
|
+
context: { responseMessages: DeviceMessage[]; device: Device }
|
|
328
|
+
) {
|
|
329
|
+
_console.log(`clientDeviceMessage ${messageType} (${dataView.byteLength} bytes)`);
|
|
330
|
+
switch (messageType) {
|
|
331
|
+
case "smp":
|
|
332
|
+
context.device.connectionManager!.sendSmpMessage(dataView.buffer);
|
|
333
|
+
break;
|
|
334
|
+
case "tx":
|
|
335
|
+
context.device.connectionManager!.sendTxData(dataView.buffer);
|
|
336
|
+
break;
|
|
337
|
+
default:
|
|
338
|
+
context.responseMessages.push(this.#createDeviceMessage(context.device, messageType));
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export default BaseServer;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { DeviceEventTypes } from "../Device.ts";
|
|
2
|
+
import { ConnectionMessageType, ConnectionMessageTypes } from "../connection/BaseConnectionManager.ts";
|
|
3
|
+
import { concatenateArrayBuffers } from "../utils/ArrayBufferUtils.ts";
|
|
4
|
+
import { createConsole } from "../utils/Console.ts";
|
|
5
|
+
import { DeviceEventType } from "../Device.ts";
|
|
6
|
+
|
|
7
|
+
const _console = createConsole("ServerUtils", { log: false });
|
|
8
|
+
|
|
9
|
+
export const ServerMessageTypes = [
|
|
10
|
+
"isScanningAvailable",
|
|
11
|
+
"isScanning",
|
|
12
|
+
"startScan",
|
|
13
|
+
"stopScan",
|
|
14
|
+
"discoveredDevice",
|
|
15
|
+
"discoveredDevices",
|
|
16
|
+
"expiredDiscoveredDevice",
|
|
17
|
+
"connectToDevice",
|
|
18
|
+
"disconnectFromDevice",
|
|
19
|
+
"connectedDevices",
|
|
20
|
+
"deviceMessage",
|
|
21
|
+
] as const;
|
|
22
|
+
export type ServerMessageType = (typeof ServerMessageTypes)[number];
|
|
23
|
+
|
|
24
|
+
export const DeviceMessageTypes = ["connectionStatus", "batteryLevel", "deviceInformation", "rx", "smp"] as const;
|
|
25
|
+
export type DeviceMessageType = (typeof DeviceMessageTypes)[number];
|
|
26
|
+
|
|
27
|
+
// MESSAGING
|
|
28
|
+
|
|
29
|
+
export type MessageLike = number | number[] | ArrayBufferLike | DataView | boolean | string | any;
|
|
30
|
+
|
|
31
|
+
export interface Message<MessageType extends string> {
|
|
32
|
+
type: MessageType;
|
|
33
|
+
data?: MessageLike | MessageLike[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function createMessage<MessageType extends string>(
|
|
37
|
+
enumeration: readonly MessageType[],
|
|
38
|
+
...messages: (Message<MessageType> | MessageType)[]
|
|
39
|
+
) {
|
|
40
|
+
_console.log("createMessage", ...messages);
|
|
41
|
+
|
|
42
|
+
const messageBuffers = messages.map((message) => {
|
|
43
|
+
if (typeof message == "string") {
|
|
44
|
+
message = { type: message };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (message.data != undefined) {
|
|
48
|
+
if (!Array.isArray(message.data)) {
|
|
49
|
+
message.data = [message.data];
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
message.data = [];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const messageDataArrayBuffer = concatenateArrayBuffers(...message.data);
|
|
56
|
+
const messageDataArrayBufferByteLength = messageDataArrayBuffer.byteLength;
|
|
57
|
+
|
|
58
|
+
_console.assertEnumWithError(message.type, enumeration);
|
|
59
|
+
const messageTypeEnum = enumeration.indexOf(message.type);
|
|
60
|
+
|
|
61
|
+
const messageDataLengthDataView = new DataView(new ArrayBuffer(2));
|
|
62
|
+
messageDataLengthDataView.setUint16(0, messageDataArrayBufferByteLength, true);
|
|
63
|
+
|
|
64
|
+
return concatenateArrayBuffers(messageTypeEnum, messageDataLengthDataView, messageDataArrayBuffer);
|
|
65
|
+
});
|
|
66
|
+
_console.log("messageBuffers", ...messageBuffers);
|
|
67
|
+
return concatenateArrayBuffers(...messageBuffers);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export type ServerMessage = ServerMessageType | Message<ServerMessageType>;
|
|
71
|
+
export function createServerMessage(...messages: ServerMessage[]) {
|
|
72
|
+
_console.log("createServerMessage", ...messages);
|
|
73
|
+
return createMessage(ServerMessageTypes, ...messages);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export type DeviceMessage = DeviceEventType | Message<DeviceEventType>;
|
|
77
|
+
export function createDeviceMessage(...messages: DeviceMessage[]) {
|
|
78
|
+
_console.log("createDeviceMessage", ...messages);
|
|
79
|
+
return createMessage(DeviceEventTypes, ...messages);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export type ClientDeviceMessage = ConnectionMessageType | Message<ConnectionMessageType>;
|
|
83
|
+
export function createClientDeviceMessage(...messages: ClientDeviceMessage[]) {
|
|
84
|
+
_console.log("createClientDeviceMessage", ...messages);
|
|
85
|
+
return createMessage(ConnectionMessageTypes, ...messages);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// STATIC MESSAGES
|
|
89
|
+
export const isScanningAvailableRequestMessage = createServerMessage("isScanningAvailable");
|
|
90
|
+
export const isScanningRequestMessage = createServerMessage("isScanning");
|
|
91
|
+
export const startScanRequestMessage = createServerMessage("startScan");
|
|
92
|
+
export const stopScanRequestMessage = createServerMessage("stopScan");
|
|
93
|
+
export const discoveredDevicesMessage = createServerMessage("discoveredDevices");
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { concatenateArrayBuffers, dataToArrayBuffer } from "../../utils/ArrayBufferUtils.ts";
|
|
2
|
+
import { createConsole } from "../../utils/Console.ts";
|
|
3
|
+
import { addEventListeners, removeEventListeners } from "../../utils/EventUtils.ts";
|
|
4
|
+
import { parseMessage } from "../../utils/ParseUtils.ts";
|
|
5
|
+
import BaseServer from "../BaseServer.ts";
|
|
6
|
+
import {
|
|
7
|
+
createUDPServerMessage,
|
|
8
|
+
pongUDPClientTimeout,
|
|
9
|
+
removeUDPClientTimeout,
|
|
10
|
+
udpPongMessage,
|
|
11
|
+
UDPServerMessageType,
|
|
12
|
+
UDPServerMessageTypes,
|
|
13
|
+
} from "./UDPUtils.ts";
|
|
14
|
+
|
|
15
|
+
/** NODE_START */
|
|
16
|
+
import type * as dgram from "dgram";
|
|
17
|
+
import Timer from "../../utils/Timer.ts";
|
|
18
|
+
/** NODE_END */
|
|
19
|
+
|
|
20
|
+
const _console = createConsole("UDPServer", { log: true });
|
|
21
|
+
|
|
22
|
+
interface UDPClient extends dgram.RemoteInfo {
|
|
23
|
+
receivePort?: number;
|
|
24
|
+
isAlive?: boolean;
|
|
25
|
+
removeSelfTimer: Timer;
|
|
26
|
+
lastTimeSentData: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface UDPClientContext {
|
|
30
|
+
client: UDPClient;
|
|
31
|
+
responseMessages: (ArrayBuffer | undefined)[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
class UDPServer extends BaseServer {
|
|
35
|
+
// CLIENTS
|
|
36
|
+
#clients: UDPClient[] = [];
|
|
37
|
+
get numberOfClients() {
|
|
38
|
+
return this.#clients.length;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
#getClientByRemoteInfo(remoteInfo: dgram.RemoteInfo, createIfNotFound = false) {
|
|
42
|
+
const { address, port } = remoteInfo;
|
|
43
|
+
let client = this.#clients.find((client) => client.address == address && client.port == port);
|
|
44
|
+
if (!client && createIfNotFound) {
|
|
45
|
+
client = {
|
|
46
|
+
...remoteInfo,
|
|
47
|
+
isAlive: true,
|
|
48
|
+
removeSelfTimer: new Timer(() => this.#removeClient(client!), removeUDPClientTimeout),
|
|
49
|
+
lastTimeSentData: 0,
|
|
50
|
+
};
|
|
51
|
+
_console.log("created new client", client);
|
|
52
|
+
|
|
53
|
+
this.#clients.push(client);
|
|
54
|
+
_console.log(`currently have ${this.numberOfClients} clients`);
|
|
55
|
+
this.dispatchEvent("clientConnected", { client });
|
|
56
|
+
}
|
|
57
|
+
return client;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
#remoteInfoToString(client: dgram.RemoteInfo) {
|
|
61
|
+
const { address, port } = client;
|
|
62
|
+
return `${address}:${port}`;
|
|
63
|
+
}
|
|
64
|
+
#clientToString(client: UDPClient) {
|
|
65
|
+
const { address, port, receivePort } = client;
|
|
66
|
+
return `${address}:${port}=>${receivePort}`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// UDP SOCKET
|
|
70
|
+
|
|
71
|
+
#socket?: dgram.Socket;
|
|
72
|
+
get socket() {
|
|
73
|
+
return this.#socket;
|
|
74
|
+
}
|
|
75
|
+
set socket(newSocket) {
|
|
76
|
+
if (this.#socket == newSocket) {
|
|
77
|
+
_console.log("redundant udp socket assignment");
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
_console.log("assigning udp socket...");
|
|
81
|
+
|
|
82
|
+
if (this.#socket) {
|
|
83
|
+
_console.log("clearing existing udp socket...");
|
|
84
|
+
removeEventListeners(this.#socket, this.#boundSocketListeners);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
addEventListeners(newSocket, this.#boundSocketListeners);
|
|
88
|
+
this.#socket = newSocket;
|
|
89
|
+
|
|
90
|
+
_console.log("assigned udp socket");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// UDP SOCKET LISTENERS
|
|
94
|
+
|
|
95
|
+
#boundSocketListeners = {
|
|
96
|
+
close: this.#onSocketClose.bind(this),
|
|
97
|
+
connect: this.#onSocketConnect.bind(this),
|
|
98
|
+
error: this.#onSocketError.bind(this),
|
|
99
|
+
listening: this.#onSocketListening.bind(this),
|
|
100
|
+
message: this.#onSocketMessage.bind(this),
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
#onSocketClose() {
|
|
104
|
+
_console.log("socket close");
|
|
105
|
+
}
|
|
106
|
+
#onSocketConnect() {
|
|
107
|
+
_console.log("socket connect");
|
|
108
|
+
}
|
|
109
|
+
#onSocketError(error: Error) {
|
|
110
|
+
_console.error("socket error", error);
|
|
111
|
+
}
|
|
112
|
+
#onSocketListening() {
|
|
113
|
+
const address = this.#socket!.address();
|
|
114
|
+
_console.log(`socket listening on port ${address.address}:${address.port}`);
|
|
115
|
+
}
|
|
116
|
+
#onSocketMessage(message: Buffer, remoteInfo: dgram.RemoteInfo) {
|
|
117
|
+
_console.log(`received ${message.length} bytes from ${this.#remoteInfoToString(remoteInfo)}`);
|
|
118
|
+
const client = this.#getClientByRemoteInfo(remoteInfo, true);
|
|
119
|
+
if (!client) {
|
|
120
|
+
_console.error("no client found");
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
client.removeSelfTimer.restart();
|
|
124
|
+
const dataView = new DataView(dataToArrayBuffer(message));
|
|
125
|
+
this.#onClientData(client, dataView);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// PARSING
|
|
129
|
+
#onClientData(client: UDPClient, dataView: DataView) {
|
|
130
|
+
_console.log(`parsing ${dataView.byteLength} bytes from ${this.#clientToString(client)}`, dataView.buffer);
|
|
131
|
+
let responseMessages: ArrayBuffer[] = [];
|
|
132
|
+
parseMessage(
|
|
133
|
+
dataView,
|
|
134
|
+
UDPServerMessageTypes,
|
|
135
|
+
this.#onClientUDPMessage.bind(this),
|
|
136
|
+
{ responseMessages, client },
|
|
137
|
+
true
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
responseMessages = responseMessages.filter(Boolean);
|
|
141
|
+
|
|
142
|
+
if (responseMessages.length == 0) {
|
|
143
|
+
_console.log("no response to send");
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (client.receivePort == undefined) {
|
|
148
|
+
_console.log("client has no defined receivePort");
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const response = concatenateArrayBuffers(responseMessages);
|
|
153
|
+
_console.log(`responding with ${response.byteLength} bytes...`, response);
|
|
154
|
+
this.#sendToClient(client, response);
|
|
155
|
+
}
|
|
156
|
+
#onClientUDPMessage(messageType: UDPServerMessageType, dataView: DataView, context: UDPClientContext) {
|
|
157
|
+
const { client, responseMessages } = context;
|
|
158
|
+
_console.log(`received "${messageType}" message from ${client.address}:${client.port}`);
|
|
159
|
+
switch (messageType) {
|
|
160
|
+
case "ping":
|
|
161
|
+
responseMessages.push(this.#createPongMessage(context));
|
|
162
|
+
break;
|
|
163
|
+
case "pong":
|
|
164
|
+
break;
|
|
165
|
+
case "setRemoteReceivePort":
|
|
166
|
+
responseMessages.push(this.#parseRemoteReceivePort(dataView, client));
|
|
167
|
+
break;
|
|
168
|
+
case "serverMessage":
|
|
169
|
+
const responseMessage = this.parseClientMessage(dataView);
|
|
170
|
+
if (responseMessage) {
|
|
171
|
+
responseMessages.push(createUDPServerMessage({ type: "serverMessage", data: responseMessage }));
|
|
172
|
+
}
|
|
173
|
+
break;
|
|
174
|
+
|
|
175
|
+
default:
|
|
176
|
+
_console.error(`uncaught messageType "${messageType}"`);
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
#createPongMessage(context: UDPClientContext) {
|
|
182
|
+
const { client } = context;
|
|
183
|
+
// TODO: - no need to ping if streaming sensor data
|
|
184
|
+
return udpPongMessage;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
#parseRemoteReceivePort(dataView: DataView, client: UDPClient) {
|
|
188
|
+
const receivePort = dataView.getUint16(0);
|
|
189
|
+
client.receivePort = receivePort;
|
|
190
|
+
_console.log(`updated ${client.address}:${client.port} receivePort to ${receivePort}`);
|
|
191
|
+
const responseDataView = new DataView(new ArrayBuffer(2));
|
|
192
|
+
responseDataView.setUint16(0, client.receivePort);
|
|
193
|
+
return createUDPServerMessage({ type: "setRemoteReceivePort", data: responseDataView });
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// CLIENT MESSAGING
|
|
197
|
+
#sendToClient(client: UDPClient, message: ArrayBuffer) {
|
|
198
|
+
_console.log(`sending ${message.byteLength} bytes to ${this.#clientToString(client)}...`);
|
|
199
|
+
try {
|
|
200
|
+
this.#socket!.send(new Uint8Array(message), client.receivePort, client.address, (error, bytes) => {
|
|
201
|
+
if (error) {
|
|
202
|
+
_console.error("error sending data", error);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
_console.log(`sent ${bytes} bytes`);
|
|
206
|
+
client.lastTimeSentData = Date.now();
|
|
207
|
+
});
|
|
208
|
+
} catch (error) {
|
|
209
|
+
_console.error("serious error sending data", error);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
broadcastMessage(message: ArrayBuffer) {
|
|
213
|
+
super.broadcastMessage(message);
|
|
214
|
+
this.#clients.forEach((client) => {
|
|
215
|
+
this.#sendToClient(client, createUDPServerMessage({ type: "serverMessage", data: message }));
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// REMOVE CLIENT
|
|
220
|
+
#removeClient(client: UDPClient) {
|
|
221
|
+
_console.log(`removing client ${this.#clientToString(client)}...`);
|
|
222
|
+
client.removeSelfTimer.stop();
|
|
223
|
+
this.#clients = this.#clients.filter((_client) => _client != client);
|
|
224
|
+
_console.log(`currently have ${this.numberOfClients} clients`);
|
|
225
|
+
this.dispatchEvent("clientDisconnected", { client });
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export default UDPServer;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createConsole } from "../../utils/Console.ts";
|
|
2
|
+
import { createMessage, Message } from "../ServerUtils.ts";
|
|
3
|
+
|
|
4
|
+
const _console = createConsole("UDPUtils", { log: false });
|
|
5
|
+
|
|
6
|
+
export const pongUDPClientTimeout = 2_000;
|
|
7
|
+
export const removeUDPClientTimeout = 3_000;
|
|
8
|
+
|
|
9
|
+
export const UDPServerMessageTypes = ["ping", "pong", "setRemoteReceivePort", "serverMessage"] as const;
|
|
10
|
+
export type UDPServerMessageType = (typeof UDPServerMessageTypes)[number];
|
|
11
|
+
|
|
12
|
+
export type UDPServerMessage = UDPServerMessageType | Message<UDPServerMessageType>;
|
|
13
|
+
export function createUDPServerMessage(...messages: UDPServerMessage[]) {
|
|
14
|
+
_console.log("createUDPServerMessage", ...messages);
|
|
15
|
+
return createMessage(UDPServerMessageTypes, ...messages);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// STATIC MESSAGES
|
|
19
|
+
export const udpPingMessage = createUDPServerMessage("ping");
|
|
20
|
+
export const udpPongMessage = createUDPServerMessage("pong");
|