@2702rebels/ntcore 1.0.1 → 1.0.2
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/index.cjs +13 -8
- package/dist/index.d.cts +201 -201
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +201 -201
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +12 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +2 -2
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/NT4Spec.ts","../src/NT4Subscription.ts","../src/NT4Topic.ts","../src/NT4WebSocketAsync.ts","../src/NT4Client.ts"],"sourcesContent":["import type { SubscriptionOptions, TopicProperties } from \"./NT4Types\";\n\n/**\n * Network constants.\n */\nexport const NetworkSpecs = {\n /** NT4 port for unsecure (ws://) connections */\n portUnsecure: 5810,\n\n /** NT4 port for secure (wss://) connections */\n portSecure: 5811,\n\n /** Subprotocol v4.1 */\n protocol41: \"v4.1.networktables.first.wpi.edu\",\n\n /** Subprotocol v4.0 */\n protocol40: \"networktables.first.wpi.edu\",\n\n /** Subprotocol RTT */\n protocolRTT: \"rtt.networktables.first.wpi.edu\",\n\n /** v4.0 ping period in milliseconds */\n pingPeriod40: 1000,\n\n /** v4.0 ping timeout in milliseconds */\n pingTimeout40: 3000,\n\n /** v4.1 ping period in milliseconds */\n pingPeriod41: 250,\n\n /** v4.1 ping timeout in milliseconds */\n pingTimeout41: 1000,\n};\n\n/**\n * Supported data types mapped to protocol identifiers.\n *\n * https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#supported-data-types\n */\nexport const DataTypeCodes = {\n boolean: 0,\n double: 1,\n int: 2,\n float: 3,\n string: 4,\n json: 4,\n raw: 5,\n rpc: 5,\n msgpack: 5,\n protobuf: 5,\n \"boolean[]\": 16,\n \"double[]\": 17,\n \"int[]\": 18,\n \"float[]\": 19,\n \"string[]\": 20,\n} as const;\n\nexport type DataType = keyof typeof DataTypeCodes;\n\n/**\n * Publish Request Message (`publish`)\n *\n * https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#publish-request-message-publish\n */\nexport type PublishRequestMessage = {\n /**\n * **Publish name**\n *\n * The topic name being published.\n */\n name: string;\n\n /**\n * **Published UID**\n *\n * A client-generated unique identifier for this publisher. Use the same UID later to unpublish.\n * This is also the identifier that the client will use in MessagePack messages for this topic.\n */\n pubuid: number;\n\n /**\n * **Type of data**\n *\n * The requested data type (as a string).\n * If the topic is newly created (e.g. there are no other publishers) this sets the value type.\n * If the topic was previously published, this is ignored.\n */\n type: string;\n\n /**\n * **Properties**\n *\n * Initial topic properties.\n * If the topic is newly created (e.g. there are no other publishers) this sets the topic properties.\n * If the topic was previously published, this is ignored.\n */\n properties: TopicProperties;\n};\n\n/**\n * Publish Release Message (`unpublish`)\n *\n * https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#publish-release-message-unpublish\n */\nexport type PublishReleaseMessage = {\n /**\n * **Publisher UID**\n *\n * The same unique identifier passed to the `publish` message.\n */\n pubuid: number;\n};\n\n/**\n * Set Properties Message (`setproperties`)\n *\n * https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#set-properties-message-setproperties\n */\nexport type SetPropertiesMessage = {\n /**\n * **Topic name**\n */\n name: string;\n\n /**\n * **Properties to update**\n */\n update: TopicProperties;\n};\n\n/**\n * Subscribe Message (`subscribe`)\n *\n * https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#subscribe-message-subscribe\n */\nexport type SubscribeMessage = {\n /**\n * **Array of topic names or prefixes**\n *\n * One or more topic names or prefixes (if the `prefix` option is true) to start receiving messages for.\n */\n topics: Array<string>;\n\n /**\n * **Subscription UID**\n *\n * A client-generated unique identifier for this subscription. Use the same UID later to unsubscribe.\n */\n subuid: number;\n\n /**\n * **Options**\n */\n options: SubscriptionOptions;\n};\n\n/**\n * Unsubscribe Message (`unsubscribe`)\n *\n * https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#unsubscribe-message-unsubscribe\n */\nexport type UnsubscribeMessage = {\n /**\n * **Subscription UID**\n *\n * The same unique identifier passed to the `subscribe` message.\n */\n subuid: number;\n};\n\n/**\n * Topic Announcement Message (`announce`)\n *\n * https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#topic-announcement-message-announce\n */\nexport type TopicAnnouncementMessage = {\n /**\n * **Topic name**\n */\n name: string;\n\n /**\n * **Topic ID**\n *\n * The identifier that the server will use in MessagePack messages for this topic.\n */\n id: number;\n\n /**\n * **Data type**\n *\n * The data type for the topic (as a string).\n */\n type: string;\n\n /**\n * **Publisher UID**\n *\n * If this message was sent in response to a publish message,\n * the Publisher UID provided in that message. Otherwise absent.\n */\n pubuid?: number;\n\n /**\n * **Properties**\n */\n properties: TopicProperties;\n};\n\n/**\n * Topic Removed Message (`unannounce`)\n *\n * https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#topic-removed-message-unannounce\n */\nexport type TopicRemovedMessage = {\n /**\n * **Topic name**\n */\n name: string;\n\n /**\n * **Topic ID**\n *\n * The identifier that the server was using for value updates.\n */\n id: number;\n};\n\n/**\n * Properties Update Message (`properties`)\n *\n * https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#properties-update-message-properties\n */\nexport type PropertiesUpdateMessage = {\n /**\n * **Topic name**\n */\n name: string;\n\n /**\n * **Acknowledgement**\n *\n * `True` if this message is in response to a `setproperties` message from the same client.\n * Otherwise absent.\n */\n ack: boolean;\n\n /**\n * **Properties to set**\n */\n update: TopicProperties;\n};\n\n/**\n * Supported text data frames.\n *\n * https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#text-data-frames\n */\nexport type TextDataFrame =\n | {\n method: \"publish\";\n params: PublishRequestMessage;\n }\n | {\n method: \"unpublish\";\n params: PublishReleaseMessage;\n }\n | {\n method: \"setproperties\";\n params: SetPropertiesMessage;\n }\n | {\n method: \"subscribe\";\n params: SubscribeMessage;\n }\n | {\n method: \"unsubscribe\";\n params: UnsubscribeMessage;\n }\n | {\n method: \"announce\";\n params: TopicAnnouncementMessage;\n }\n | {\n method: \"unannounce\";\n params: TopicRemovedMessage;\n }\n | {\n method: \"properties\";\n params: PropertiesUpdateMessage;\n };\n","import type { SubscriptionOptions } from \"./NT4Types\";\n\n/** Contains information about NT4 subscription. */\nexport class NT4Subscription {\n /** Subscription UID. */\n public readonly uid: number;\n\n /** Subscription options. */\n public readonly options: SubscriptionOptions;\n\n /** Subscribed topic names or prefixes. */\n public readonly topics: Set<string>;\n\n constructor(uid: number, topics: Iterable<string>, options?: SubscriptionOptions) {\n this.uid = uid;\n this.topics = new Set(typeof topics === \"string\" ? [topics] : topics);\n this.options = options ?? {};\n }\n}\n","import { DataTypeCodes } from \"./NT4Spec\";\n\nimport type { DataType } from \"./NT4Spec\";\nimport type { TopicProperties } from \"./NT4Types\";\n\n/** Contains information about NT4 topic. */\nexport class NT4Topic {\n /** Topic UID. */\n public readonly uid: number;\n\n /** Topic name. */\n public readonly name: string;\n\n /** Topic data type. */\n public readonly type: string;\n\n /** Topic properties. */\n public readonly properties: TopicProperties;\n\n /** Publisher UID. */\n public pubuid?: number;\n\n /** Last retained value. */\n public retainedValue?: unknown;\n\n /** Timestamp of {@link retainedValue}. */\n public retainedTimestamp?: number;\n\n constructor(uid: number, name: string, type: string, properties?: TopicProperties, pubuid?: number) {\n this.uid = uid;\n this.name = name;\n this.type = type;\n this.properties = properties ?? {};\n this.pubuid = pubuid;\n this.typeCode = DataTypeCodes[type as DataType] ?? 5; // defaults to binary\n }\n\n /** Merges properties with this topic. */\n public mergeProperties(properties: TopicProperties) {\n Object.entries(properties).forEach(([key, value]) => {\n if (value === null) {\n delete this.properties[key];\n } else {\n this.properties[key] = value;\n }\n });\n }\n\n /** Updates retained value if the incoming timestamp is newer (greater-than or equal-to). */\n public updateRetainedValue(value: unknown, timestamp: number) {\n if (this.retainedTimestamp == null || timestamp >= this.retainedTimestamp) {\n this.retainedValue = value;\n this.retainedTimestamp = timestamp;\n }\n }\n\n /** Removes retained value except for \"weak\" default value. */\n public removeRetainedValue() {\n if (this.retainedTimestamp != 0) {\n this.retainedValue = undefined;\n this.retainedTimestamp = undefined;\n }\n }\n\n /** Resets retained value timestamp except for \"weak\" default value. */\n public resetRetainedTimestamp() {\n if (this.retainedTimestamp != null && this.retainedTimestamp > 1) {\n this.retainedTimestamp = 1;\n }\n }\n\n /** Determines whether topic can accept the specified value. */\n public canAccept(value: unknown) {\n switch (this.typeCode) {\n // primitives\n case DataTypeCodes.boolean:\n return typeof value === \"boolean\";\n case DataTypeCodes.double:\n case DataTypeCodes.float:\n return typeof value === \"number\";\n case DataTypeCodes.int:\n return typeof value === \"number\" && Number.isInteger(value);\n case DataTypeCodes.string:\n return typeof value === \"string\";\n\n // arrays of primitives\n case DataTypeCodes[\"boolean[]\"]:\n return Array.isArray(value) && value.every((v) => typeof v === \"boolean\");\n case DataTypeCodes[\"double[]\"]:\n case DataTypeCodes[\"float[]\"]:\n return Array.isArray(value) && value.every((v) => typeof v === \"number\");\n case DataTypeCodes[\"int[]\"]:\n return Array.isArray(value) && value.every((v) => typeof v === \"number\" && Number.isInteger(v));\n case DataTypeCodes[\"string[]\"]:\n return Array.isArray(value) && value.every((v) => typeof v === \"string\");\n\n case DataTypeCodes.json:\n return value !== null && (typeof value === \"object\" || typeof value === \"string\");\n\n default:\n return true;\n }\n }\n\n /** Topic type code. For internal use only. */\n public readonly typeCode: number;\n}\n","import { NetworkSpecs } from \"./NT4Spec\";\n\nimport type { TextDataFrame } from \"./NT4Spec\";\n\n// version 4.1 -- v4.1.networktables.first.wpi.edu\n// version 4.0 -- networktables.first.wpi.edu\nconst PROTOCOL_DATA = [NetworkSpecs.protocol41, NetworkSpecs.protocol40];\n\n// https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#rtt-subprotocol\nconst PROTOCOL_RTT = [NetworkSpecs.protocolRTT];\n\n/**\n * A wrapper for `WebSocket` that implements async `connect` and `close` methods.\n *\n * This class manages the lifecycle of the raw websocket internally, creating and\n * destroying the websocket as necessary when establishing and closing the connection.\n */\nexport class NT4WebSocketAsync {\n private url: string;\n private rtt: boolean;\n private socket: WebSocket | null = null;\n private onMessage?: (_: MessageEvent) => void;\n private onClose?: (_: CloseEvent) => void;\n\n /**\n * Creates a `WebSocket` wrapper to be used for NT4 communication channel.\n * This constructor does not initiate the connection, see `connect`.\n *\n * @param address server network address\n * @param port sever port\n * @param clientId client identifier\n * @param options connection options\n */\n constructor(\n address: string,\n port: number,\n clientId: string,\n options: {\n /** Callback invoked when socket receives message. */\n onMessage?: (ev: MessageEvent) => void;\n /** Callback invoked when socket closes. */\n onClose?: (ev: CloseEvent) => void;\n /** `true` to use secure protocol/port; `false` to use unsecure protocol/port (default). */\n secure?: boolean;\n /** `true` to create RTT subprotocol channel; `false` to create default data channel (default). */\n rtt?: boolean;\n }\n ) {\n const schema = options.secure ? \"wss\" : \"ws\";\n\n this.url = `${schema}://${address}:${port}/nt/${clientId}`;\n this.rtt = options.rtt ?? false;\n this.onMessage = options.onMessage;\n this.onClose = options.onClose;\n }\n\n private onMessageLocal(ev: MessageEvent, source: WebSocket) {\n if (this.onMessage && this.socket === source) {\n this.onMessage(ev);\n }\n }\n\n private onCloseLocal(ev: CloseEvent, source: WebSocket) {\n if (this.onClose && this.socket === source) {\n this.onClose(ev);\n }\n }\n\n /** Returns `true` if the socket is in the connected state. */\n public get connected() {\n return this.socket != null && this.socket.readyState === WebSocket.OPEN;\n }\n\n /** Returns `true` if the socket is in the connecting state. */\n public get connecting() {\n return this.socket != null && this.socket.readyState === WebSocket.CONNECTING;\n }\n\n /** Returns the subprotocol negotiated with the server. */\n public get protocol() {\n return this.socket?.protocol;\n }\n\n /** Sends text data frame (JSON serialized). */\n public sendText(data: TextDataFrame | Array<TextDataFrame>) {\n if (this.socket && this.socket.readyState === WebSocket.OPEN) {\n this.socket.send(JSON.stringify(Array.isArray(data) ? data : [data]));\n }\n }\n\n /** Sends binary data frame. */\n public sendBinary(data: Uint8Array) {\n if (this.socket && this.socket.readyState === WebSocket.OPEN) {\n this.socket.send(data);\n }\n }\n\n /**\n * Asynchronously terminates the connection.\n *\n * @param code optional code\n * @param reason optional reason\n */\n public async close(code?: number, reason?: string): Promise<void> {\n if (this.socket) {\n const socket = this.socket;\n this.socket = null;\n\n // websocket is closing asynchronously, we need to wait\n // for the corresponding onclose to fire:\n // attach another listener here and promisify\n return new Promise((resolve) => {\n socket.addEventListener(\"close\", () => {\n resolve();\n });\n\n socket.close(code, reason);\n });\n }\n\n return Promise.resolve();\n }\n\n /** Asynchronously initiates the connection. */\n public async connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n const socket = new WebSocket(this.url, this.rtt ? PROTOCOL_RTT : PROTOCOL_DATA);\n\n socket.binaryType = \"arraybuffer\";\n\n this.socket = socket;\n\n socket.addEventListener(\"message\", (_) => this.onMessageLocal(_, socket));\n socket.addEventListener(\"close\", (_) => this.onCloseLocal(_, socket));\n\n socket.onopen = () => {\n resolve();\n };\n\n socket.onerror = (event) => {\n reject(event);\n };\n });\n }\n}\n","import { Decoder, Encoder } from \"@msgpack/msgpack\";\n\nimport { Logger } from \"@2702rebels/logger\";\n\nimport { DataTypeCodes, NetworkSpecs } from \"./NT4Spec\";\nimport { NT4Subscription } from \"./NT4Subscription\";\nimport { NT4Topic } from \"./NT4Topic\";\nimport { NT4WebSocketAsync } from \"./NT4WebSocketAsync\";\n\nimport type { DataType, TextDataFrame } from \"./NT4Spec\";\nimport type { SubscriptionOptions, TopicProperties } from \"./NT4Types\";\n\nexport type NT4ClientConnectionState = \"disconnected\" | \"connecting\" | \"connected\";\n\nexport type NT4ClientOptions = {\n /** Indicates that secure channel should be used. */\n secure?: boolean;\n\n /**\n * Invoked when the client connects.\n */\n onConnect?: () => void;\n\n /**\n * Invoked when the client disconnects.\n */\n onDisconnect?: () => void;\n\n /**\n * Invoked when new topic is announced by the server.\n *\n * @param topic topic announced\n */\n onTopicAnnounced?: (topic: NT4Topic) => void;\n\n /**\n * Invoked when a topic is removed by the server.\n *\n * @param topic topic removed\n */\n onTopicRemoved?: (topic: NT4Topic) => void;\n\n /**\n * Invoked when a topic properties are updated by the server.\n *\n * @param topic topic updated\n */\n onTopicUpdated?: (topic: NT4Topic) => void;\n\n /**\n * Invoked when the topic receives new data.\n *\n * @param topic topic receiving data\n * @param value value received\n * @param timestamp server timestamp in microseconds\n */\n onDataReceived?: (topic: NT4Topic, value: unknown, timestamp: number) => void;\n\n /**\n * Retry policy that accepts the current number of connection attempts\n * and returns the timeout in milliseconds or -1 to abort.\n *\n * @param attempts number of subsequent connection attempts\n * @returns timeout in milliseconds or -1 to abort\n */\n retryPolicy?: (attempts: number) => number;\n\n /**\n * Overrides default timeout for ping-pong packets.\n *\n * Defaults:\n * - NT 4.0: 3000 milliseconds\n * - NT 4.1: 1000 milliseconds\n */\n pingTimeoutMilliseconds?: number;\n};\n\n/** Determines whether the specified predicate callback returns true for any element of a map. */\nfunction some<K, V>(map: Map<K, V>, predicate: (entry: V) => boolean) {\n for (const v of map.values()) {\n if (predicate(v)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Implements a NetworkTable4 client that is responsible for establishing and maintaining a connection.\n */\nexport class NT4Client {\n private readonly port: number;\n private readonly clientId: string;\n private readonly dataChannel: NT4WebSocketAsync;\n private readonly rttChannel: NT4WebSocketAsync;\n private readonly decoder = new Decoder();\n private readonly encoder = new Encoder();\n private readonly onConnect?: NT4ClientOptions[\"onConnect\"];\n private readonly onDisconnect?: NT4ClientOptions[\"onDisconnect\"];\n private readonly onTopicAnnounced?: NT4ClientOptions[\"onTopicAnnounced\"];\n private readonly onTopicRemoved?: NT4ClientOptions[\"onTopicRemoved\"];\n private readonly onTopicUpdated?: NT4ClientOptions[\"onTopicUpdated\"];\n private readonly onDataReceived?: NT4ClientOptions[\"onDataReceived\"];\n private readonly retryPolicy: Required<NT4ClientOptions>[\"retryPolicy\"];\n private pingTimeoutOverride_ms?: NT4ClientOptions[\"pingTimeoutMilliseconds\"];\n\n private subscriptions = new Map<number, NT4Subscription>();\n private publishedTopics = new Map<string, NT4Topic>();\n private announcedTopics = new Map<string, NT4Topic>();\n private announcedTopicsIndex = new Map<number, NT4Topic>();\n\n private connectionEstablished = false;\n private connectionInitiated = false;\n private connectionAttempts = 0;\n private connectionState: NT4ClientConnectionState = \"disconnected\";\n private networkLatency_μs: number | null = null;\n private serverTimeOffset_μs: number | null = null;\n private pingTimeout_ms = 0;\n private pongTimestamp_μs = 0;\n private pingIntervalId: ReturnType<typeof setInterval> | null = null;\n private pongIntervalId: ReturnType<typeof setInterval> | null = null;\n\n /** Truncated at 15 seconds exponential backoff retry policy starting at 1 second. */\n public static defaultRetryPolicy(attempts: number) {\n return Math.round(Math.min(15, Math.max(1, 1.2 ** Math.min(15, attempts))) * 1000);\n }\n\n /** Cleans up closed data channel. */\n private cleanup() {\n Logger.Default.debug(`[NT4Client] cleaning up`);\n\n if (this.pingIntervalId) {\n clearInterval(this.pingIntervalId);\n this.pingIntervalId = null;\n }\n\n if (this.pongIntervalId) {\n clearInterval(this.pongIntervalId);\n this.pongIntervalId = null;\n }\n\n this.rttChannel.close();\n\n if (this.connectionEstablished && this.onDisconnect) {\n this.onDisconnect();\n }\n\n this.connectionEstablished = false;\n this.connectionState = \"disconnected\";\n\n this.announcedTopics.clear();\n this.announcedTopicsIndex.clear();\n\n for (const topic of this.publishedTopics.values()) {\n topic.resetRetainedTimestamp();\n }\n\n this.pongTimestamp_μs = 0;\n this.serverTimeOffset_μs = null;\n this.networkLatency_μs = null;\n }\n\n /** Attempts to establish a connection, continuously retrying using policy. */\n private async connectWithRetry() {\n this.connectionState = \"connecting\";\n\n // check for cancellation after each await, since disconnect could have been called\n // while we were awaiting and therefore we should abort here or below\n if (!this.connectionInitiated) {\n Logger.Default.debug(`[NT4Client] pending cancellation, aborting...`);\n return;\n }\n\n // probe remote endpoint status (liveness check)\n let response: Response | null = null;\n try {\n response = await fetch(`http://${this.serverAddress}:${this.port}`, {\n signal: AbortSignal.timeout(250),\n });\n } catch {\n // swallow exception here; we are checking for response status below\n }\n\n if (!this.connectionInitiated) {\n Logger.Default.debug(`[NT4Client] pending cancellation, aborting...`);\n return;\n }\n\n // if endpoint seems to be alive try to establish websocket connection,\n // otherwise, wait and retry again\n if (response && response.ok) {\n try {\n Logger.Default.debug(`[NT4Client] opening channel to ${this.serverAddress}:${this.port}`);\n await this.dataChannel.connect();\n\n // reset connection attempts once the data channel is successfully connected\n this.connectionAttempts = 0;\n\n if (!this.connectionInitiated) {\n Logger.Default.debug(`[NT4Client] pending cancellation, aborting...`);\n return;\n }\n\n this.connectionEstablished = true;\n this.connectionState = \"connected\";\n\n Logger.Default.debug(\n `[NT4Client] connected to ${this.serverAddress}:${this.port}, subprotocol = ${this.dataChannel.protocol}`\n );\n\n let pingPeriod_ms = NetworkSpecs.pingPeriod40;\n this.pingTimeout_ms = this.pingTimeoutOverride_ms ?? NetworkSpecs.pingTimeout40;\n\n if (this.dataChannel.protocol === NetworkSpecs.protocol41) {\n Logger.Default.debug(`[NT4Client] server speaks v4.1, creating separate RTT channel`);\n await this.rttChannel.connect();\n\n if (!this.connectionInitiated) {\n Logger.Default.debug(`[NT4Client] pending cancellation, aborting...`);\n return;\n }\n\n pingPeriod_ms = NetworkSpecs.pingPeriod41;\n this.pingTimeout_ms = this.pingTimeoutOverride_ms ?? NetworkSpecs.pingTimeout41;\n }\n\n Logger.Default.debug(`[NT4Client] sending timestamp and re-establishing subscriptions`);\n\n // send timestamp to establish RTT\n this.sendRTT();\n\n // and start periodic ping\n this.pongTimestamp_μs = 0;\n this.pingIntervalId = setInterval(() => this.sendRTT(), pingPeriod_ms);\n this.pongIntervalId = setInterval(() => this.checkTimeout(), pingPeriod_ms);\n\n // restore client subscriptions\n for (const subscription of this.subscriptions.values()) {\n this.subscribeCore(subscription);\n }\n\n // restore client publishers\n // retained values won't be sent until clock is synchronized\n for (const topic of this.publishedTopics.values()) {\n this.publishCore(topic);\n }\n\n if (this.onConnect) {\n this.onConnect();\n }\n\n return;\n } catch (exception) {\n Logger.Default.error(`[NT4Client] failed to open channel`, exception);\n\n await this.rttChannel.close();\n await this.dataChannel.close();\n }\n }\n\n // retry logic\n this.connectionAttempts++;\n const timeout = this.retryPolicy(this.connectionAttempts);\n\n if (timeout < 0) {\n Logger.Default.debug(`[NT4Client] aborting per retry policy after ${this.connectionAttempts} attempts`);\n this.disconnect();\n return;\n }\n\n Logger.Default.debug(`[NT4Client] retrying connection in ${timeout.toFixed(0)}ms`);\n setTimeout(() => this.connectWithRetry(), timeout);\n }\n\n /** Invoked once the client-server clock is synchronized. */\n private onClockSynchronized() {\n Logger.Default.debug(`[NT4Client] clock synchronized, offset = ${this.serverTimeOffset_μs}`);\n\n for (const topic of this.publishedTopics.values()) {\n if (topic.retainedTimestamp != null && topic.retainedValue != null) {\n this.sendValueCore(\n topic,\n topic.retainedValue,\n topic.retainedTimestamp === 1 ? (this.serverTimeMicroseconds() ?? 1) : topic.retainedTimestamp\n );\n }\n }\n }\n\n /** Consumes received data frame. */\n private processDataFrame(data: string | BufferSource) {\n if (typeof data === \"string\") {\n let array: Array<TextDataFrame> | undefined;\n try {\n array = JSON.parse(data);\n if (!Array.isArray(array)) {\n Logger.Default.error(`[NT4Client] unexpected text frame, JSON must be an array, frame ignored`);\n return;\n }\n } catch (exception) {\n Logger.Default.error(`[NT4Client] unexpected text frame, JSON is malformed, frame ignored`, exception);\n return;\n }\n\n array.forEach((d) => {\n if (typeof d !== \"object\" || !(\"method\" in d) || !(\"params\" in d)) {\n Logger.Default.error(`[NT4Client] malformed message in text frame, message ignored`);\n return;\n }\n\n const { method, params } = d;\n if (typeof method !== \"string\" || typeof params !== \"object\") {\n Logger.Default.error(`[NT4Client] malformed message in text frame, message ignored`);\n return;\n }\n\n switch (method) {\n case \"announce\": {\n const topic = new NT4Topic(params.id, params.name, params.type, params.properties, params.pubuid);\n this.announcedTopics.set(topic.name, topic);\n this.announcedTopicsIndex.set(topic.uid, topic);\n\n if (this.onTopicAnnounced) {\n this.onTopicAnnounced(topic);\n }\n return;\n }\n\n case \"unannounce\": {\n const topic = this.announcedTopics.get(params.name);\n\n // ignore topics that we haven't seen being announced\n if (topic) {\n this.publishedTopics.get(topic.name)?.removeRetainedValue();\n this.announcedTopics.delete(topic.name);\n this.announcedTopicsIndex.delete(topic.uid);\n\n if (this.onTopicRemoved) {\n this.onTopicRemoved(topic);\n }\n }\n return;\n }\n\n case \"properties\": {\n const topic = this.announcedTopics.get(params.name);\n // ignore topics that we haven't seen being announced\n if (topic) {\n // https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#properties-update-message-properties\n // The client shall handle the update value as follows.\n // If a property is not included in the update map, its value is not changed.\n // If a property is provided in the update map with a value of null, the property is deleted.\n topic.mergeProperties(params.update);\n\n if (this.onTopicUpdated) {\n this.onTopicUpdated(topic);\n }\n }\n return;\n }\n\n default:\n Logger.Default.error(`[NT4Client] unexpected method '${method}' in text frame message, message ignored`);\n return;\n }\n });\n } else {\n for (const d of this.decoder.decodeMulti(data)) {\n const [topicId, timestamp, typeCode, value] = d as [number, number, number, unknown];\n\n // sanity checks\n if (typeof topicId !== \"number\") {\n Logger.Default.error(`[NT4Client] unexpected non-numeric topic identifier, frame ignored`);\n return;\n }\n\n if (typeof timestamp !== \"number\") {\n Logger.Default.error(`[NT4Client] unexpected non-numeric timestamp, frame ignored`);\n return;\n }\n\n if (typeof typeCode !== \"number\") {\n Logger.Default.error(`[NT4Client] unexpected non-numeric typeCode, frame ignored`);\n return;\n }\n\n if (topicId === -1) {\n // RTT measurement\n if (typeof value !== \"number\") {\n Logger.Default.error(`[NT4Client] unexpected non-numeric value in RTT frame, message ignored`);\n continue;\n }\n\n const clientTime_μs = this.getClientTimestamp_μs();\n const latency_μs = (clientTime_μs - value) / 2.0; // network latency is estimated as half of RTT\n\n // if the RTT is less than that from previous measurements, the client shall use\n // the timestamp in the message plus ½ the RTT as the server time equivalent to\n // the current local time\n if (this.networkLatency_μs == null || latency_μs < this.networkLatency_μs) {\n const clockSynchronized = this.serverTimeOffset_μs == null;\n\n // server timestamp is in microseconds\n this.serverTimeOffset_μs = Math.round(timestamp + latency_μs - clientTime_μs);\n\n if (clockSynchronized) {\n this.onClockSynchronized();\n }\n }\n\n this.networkLatency_μs = latency_μs;\n\n // update pong timestamp (used for checking timeouts)\n this.pongTimestamp_μs = clientTime_μs;\n } else if (topicId > 0) {\n const topic = this.announcedTopicsIndex.get(topicId);\n if (topic == null) {\n Logger.Default.error(`[NT4Client] unexpected topic identifier ${topicId}, message ignored`);\n continue;\n } else if (this.onDataReceived) {\n // update locally retained value on topics published by the client\n this.publishedTopics.get(topic.name)?.updateRetainedValue(value, timestamp);\n this.onDataReceived(topic, value, timestamp);\n }\n } else {\n Logger.Default.error(`[NT4Client] invalid topic identifier ${topicId}, message ignored`);\n continue;\n }\n }\n }\n }\n\n /** Handles general data frame received. */\n private onChannelMessage = (ev: MessageEvent) => {\n this.processDataFrame(ev.data);\n };\n\n /** Handles RTT data frame received. */\n private onChannelMessageRTT = (ev: MessageEvent) => {\n if (typeof ev.data === \"string\") {\n Logger.Default.debug(`[NT4Client] unexpected text data frame on RTT channel, frame ignored`);\n return;\n }\n\n this.processDataFrame(ev.data);\n };\n\n /** Handles channel closing for whatever reason. */\n private onChannelClose = (ev: CloseEvent) => {\n Logger.Default.debug(`[NT4Client] channel closed`, ev);\n\n this.cleanup();\n this.connectWithRetry();\n };\n\n /** Returns current timestamp in microseconds. This is NOT wall clock time. */\n private getClientTimestamp_μs() {\n // monotonically increasing, microsecond precision\n return Math.round(performance.now() * 1000);\n }\n\n /** Sends data frame to publish the topic. */\n private publishCore(topic: NT4Topic) {\n this.dataChannel.sendText({\n method: \"publish\",\n params: {\n pubuid: topic.uid,\n name: topic.name,\n type: topic.type,\n properties: topic.properties,\n },\n });\n }\n\n /** Sends data frame to unpublish the topic. */\n private unpublishCore(topic: NT4Topic) {\n this.dataChannel.sendText({\n method: \"unpublish\",\n params: {\n pubuid: topic.uid,\n },\n });\n }\n\n /** Sends data frame to set topic properties. */\n private setPropertiesCore(name: string, properties: TopicProperties) {\n this.dataChannel.sendText({\n method: \"setproperties\",\n params: {\n name: name,\n update: properties,\n },\n });\n }\n\n /** Sends data frame to subscribe the subscription. */\n private subscribeCore(subscription: NT4Subscription) {\n this.dataChannel.sendText({\n method: \"subscribe\",\n params: {\n subuid: subscription.uid,\n topics: Array.from(subscription.topics),\n options: subscription.options,\n },\n });\n }\n\n /** Sends data frame to unsubscribe the subscription. */\n private unsubscribeCore(subscription: NT4Subscription) {\n this.dataChannel.sendText({\n method: \"unsubscribe\",\n params: {\n subuid: subscription.uid,\n },\n });\n }\n\n /** Sends data frame to update topic value. */\n private sendValueCore(topic: NT4Topic, value: unknown, timestamp: number) {\n const frame = this.encoder.encode([topic.uid, timestamp, topic.typeCode, value]);\n this.dataChannel.sendBinary(frame);\n }\n\n /**\n * Sends RTT measurement binary frame.\n * Uses RTT channel if available, falls back to data channel in NT4.0.\n */\n private sendRTT() {\n // https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#timestamps\n // The topic ID of -1 is reserved for timestamp communication.\n // Clients shall periodically (e.g. every few seconds) send, in a manner that\n // minimizes transmission delays, a MessagePack message with ID of -1, a timestamp of 0,\n // and an implementation-selected type and data value (typically int or float 64)\n // containing its (the client’s) current local time\n const timestamp = this.getClientTimestamp_μs();\n const frame = this.encoder.encode([-1, 0, DataTypeCodes.double, timestamp]);\n (this.rttChannel ?? this.dataChannel).sendBinary(frame);\n }\n\n /**\n * Determines whether connection times out if no message has been received\n * within the timeout interval since the last pong.\n */\n private async checkTimeout() {\n if (!this.connectionEstablished || this.pongTimestamp_μs === 0) {\n return;\n }\n\n const delta_μs = this.getClientTimestamp_μs() - this.pongTimestamp_μs;\n if (delta_μs > this.pingTimeout_ms * 1000) {\n Logger.Default.debug(\n `[NT4Client] no data received in the last ${(delta_μs / 1000).toFixed(0)}ms, connection timed out`\n );\n\n await this.dataChannel.close(4001, \"timeout\");\n this.cleanup();\n this.connectWithRetry();\n }\n }\n\n /** Generates random UID. */\n private generateUID() {\n return Math.floor(Math.random() * 0x7fffffff);\n }\n\n /** Generates random safe UID avoiding possible clashes. */\n private generateSafeUID(has: (id: number) => boolean) {\n let uid: number;\n for (uid = this.generateUID(); has(uid); uid = this.generateUID());\n return uid;\n }\n\n /** Determines whether published topic with the specified identifier exists. */\n private readonly hasPublishedTopicWithId = (id: number) => some(this.publishedTopics, (_) => _.uid === id);\n\n /** Determines whether subscription with the specified identifier exists. */\n private readonly hasSubscriptionWithId = (id: number) => this.subscriptions.has(id);\n\n /**\n * Constructs an instance of NT4Client without initiating a connection.\n *\n * @param serverAddress NT4 server network address\n * @param clientId this client identifier (cannot contain '@')\n * @param options options\n */\n constructor(serverAddress: string, clientId: string, options: NT4ClientOptions) {\n // https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#websockets-protocol-configuration\n // ...\n // The client name does not need to be unique; multiple connections to the same name are allowed;\n // the server shall ensure the name is unique (for the purposes of meta-topics) by appending a '@'\n // and a unique number (if necessary). To support this, the name provided by the client should not\n // contain an embedded '@'. Clients should provide a way to specify the resource name (in particular,\n // the client name portion).\n if (clientId.includes(\"@\")) {\n throw new Error(\"Client identifier must not contain '@' character\");\n }\n\n this.serverAddress = serverAddress;\n this.port = options.secure ? NetworkSpecs.portSecure : NetworkSpecs.portUnsecure;\n this.clientId = clientId;\n this.onConnect = options.onConnect;\n this.onDisconnect = options.onDisconnect;\n this.onTopicAnnounced = options.onTopicAnnounced;\n this.onTopicRemoved = options.onTopicRemoved;\n this.onTopicUpdated = options.onTopicUpdated;\n this.onDataReceived = options.onDataReceived;\n this.retryPolicy = options.retryPolicy ?? NT4Client.defaultRetryPolicy;\n this.pingTimeoutOverride_ms = options.pingTimeoutMilliseconds;\n\n // data messages channel\n this.dataChannel = new NT4WebSocketAsync(this.serverAddress, this.port, this.clientId, {\n secure: options.secure,\n onMessage: this.onChannelMessage,\n onClose: this.onChannelClose,\n });\n\n // RTT messages channel (only used in NT4.1)\n this.rttChannel = new NT4WebSocketAsync(this.serverAddress, this.port, this.clientId, {\n rtt: true,\n secure: options.secure,\n onMessage: this.onChannelMessageRTT,\n });\n }\n\n /**\n * Returns NT4 server network address the client is using.\n */\n public readonly serverAddress: string;\n\n /**\n * Returns current connection state.\n */\n public get state() {\n return this.connectionState;\n }\n\n /**\n * Returns current network latency in microseconds if known.\n */\n public get networkLatencyMicroseconds() {\n return this.networkLatency_μs;\n }\n\n /**\n * Returns server time in microseconds that is current or\n * based on the specified client (local) time.\n *\n * Returns `null` if the server time offset is not established.\n */\n public serverTimeMicroseconds(clientTimeMicroseconds?: number) {\n return this.serverTimeOffset_μs != null\n ? Math.round(\n (clientTimeMicroseconds != null ? clientTimeMicroseconds : this.getClientTimestamp_μs()) +\n this.serverTimeOffset_μs\n )\n : null;\n }\n\n /**\n * Initiates the connection.\n * Once established the connection will be automatically kept alive by reconnecting.\n */\n public connect() {\n if (this.connectionInitiated) {\n return;\n }\n\n Logger.Default.debug(`[NT4Client] connection initiated`);\n this.connectionInitiated = true;\n this.connectWithRetry();\n }\n\n /**\n * Terminates the connection.\n * The client can be reconnected manually by calling {@link connect} again.\n */\n public disconnect() {\n if (!this.connectionInitiated) {\n return;\n }\n\n Logger.Default.debug(`[NT4Client] connection cancelled`);\n this.connectionInitiated = false;\n this.dataChannel.close();\n this.rttChannel.close();\n }\n\n /**\n * Creates a new subscription.\n *\n * The subscription can be created regardless of connectivity state.\n * All client subscriptions are automatically restored once connection\n * has been established. This is also true for reconnects following\n * loss of connectivity or deliberate disconnects.\n *\n * Caller can {@link unsubscribe} by passing the return value of this call or\n * invoke {@link unsubscribeAll} to remove all existing client subscriptions.\n *\n * @param topics topics or prefixes to include in the subscription\n * @param options subscription options\n * @returns A subscription instance\n */\n public subscribe(topics: Iterable<string>, options: SubscriptionOptions) {\n const uid = this.generateSafeUID(this.hasSubscriptionWithId);\n const subscription = new NT4Subscription(uid, topics, options);\n this.subscriptions.set(subscription.uid, subscription);\n\n if (this.connectionEstablished) {\n this.subscribeCore(subscription);\n }\n\n return subscription;\n }\n\n /**\n * Unsubscribes an existing subscription.\n *\n * @param arg subscription or subscription identifier\n * @returns `true` if the subscription was successfully unsubscribed, `false` if the subscription was not found\n */\n public unsubscribe(arg: NT4Subscription | number) {\n const subscription = typeof arg === \"number\" ? this.subscriptions.get(arg) : arg;\n if (!subscription) {\n return false;\n }\n\n this.subscriptions.delete(subscription.uid);\n if (this.connectionEstablished) {\n this.unsubscribeCore(subscription);\n }\n\n return true;\n }\n\n /**\n * Unsubscribes all existing subscriptions.\n */\n public unsubscribeAll() {\n for (const subscription of this.subscriptions.values()) {\n this.unsubscribe(subscription);\n }\n }\n\n /**\n * Publishes a topic with the specified name and type.\n *\n * This method registers this client as a publisher for the topic.\n * It should be called before {@link setValue} or {@link setValueAt}\n * can be used.\n *\n * Publishing a topic on a disconnected client is allowed.\n * Client will automatically register such publishers once the connection\n * has been established and send default or retained value as appropriate.\n *\n * @param topic topic name\n * @param type data type\n * @param properties properties\n */\n public publishTopic(topic: string, type: DataType | string, properties?: TopicProperties) {\n let publishedTopic = this.publishedTopics.get(topic);\n if (publishedTopic) {\n return publishedTopic;\n }\n\n const uid = this.generateSafeUID(this.hasPublishedTopicWithId);\n publishedTopic = new NT4Topic(uid, topic, type, properties, uid);\n this.publishedTopics.set(publishedTopic.name, publishedTopic);\n\n if (this.connectionEstablished) {\n this.publishCore(publishedTopic);\n }\n\n return publishedTopic;\n }\n\n /**\n * Un-publishes previously published topic.\n *\n * @param topic topic name\n * @returns `true` if the topic was successfully unpublished, `false` if the topic was not published\n */\n public unpublishTopic(topic: string) {\n const publishedTopic = this.publishedTopics.get(topic);\n if (!publishedTopic) {\n return false;\n }\n\n this.publishedTopics.delete(publishedTopic.name);\n if (this.connectionEstablished) {\n this.unpublishCore(publishedTopic);\n }\n\n return true;\n }\n\n /**\n * Determines whether client already publishes the topic.\n *\n * @param topic topic name\n */\n public isTopicPublished(topic: string) {\n return this.publishedTopics.has(topic);\n }\n\n /**\n * Sets properties for the specified topic.\n *\n * @param topic topic name\n * @param properties new properties\n */\n public setTopicProperties(topic: string, properties: TopicProperties) {\n const publishedTopic = this.publishedTopics.get(topic);\n if (publishedTopic) {\n publishedTopic.mergeProperties(properties);\n }\n\n const announcedTopic = this.announcedTopics.get(topic);\n if (announcedTopic) {\n announcedTopic.mergeProperties(properties);\n }\n\n if (this.connectionEstablished) {\n this.setPropertiesCore(topic, properties);\n }\n }\n\n /**\n * Sets persistent flag for the topic.\n *\n * @param topic topic name\n * @param value value to set\n */\n public setTopicPersistent(topic: string, value: boolean) {\n this.setTopicProperties(topic, { persistent: value });\n }\n\n /**\n * Sets retained flag for the topic.\n *\n * @param topic topic name\n * @param value value to set\n */\n public setTopicRetained(topic: string, value: boolean) {\n this.setTopicProperties(topic, { retained: value });\n }\n\n /**\n * Sends a value to the published topic using the current time.\n *\n * This client must publish the topic with {@link publishTopic} first.\n *\n * @param topic topic name\n * @param value value to send\n */\n public setValue(topic: string, value: unknown) {\n this.setValueAt(topic, value, this.serverTimeMicroseconds() ?? 1);\n }\n\n /**\n * Sends a default (\"weak\") value to the published topic.\n *\n * This client must publish the topic with {@link publishTopic} first.\n *\n * @param topic topic name\n * @param value value to send\n */\n public setDefaultValue(topic: string, value: unknown) {\n this.setValueAt(topic, value, 0);\n }\n\n /**\n * Sends a value to the published topic using the specified timestamp.\n *\n * This client must publish the topic with {@link publishTopic} first.\n *\n * @param topic topic name\n * @param value value to send\n * @param timestamp timestamp\n */\n public setValueAt(topic: string, value: unknown, timestamp: number) {\n const publishedTopic = this.publishedTopics.get(topic);\n if (!publishedTopic) {\n throw new Error(\"Topic must be published first\");\n }\n\n if (!publishedTopic.canAccept(value)) {\n throw new Error(\"Value is not compatible with topic underlying type\");\n }\n\n // timestamps in messages always use the server time base,\n // if the offset has not been established, the client retains\n // the last value and will send it once connection is restored\n publishedTopic.updateRetainedValue(value, timestamp);\n\n // update locally for announced topics\n if (this.onDataReceived) {\n const announcedTopic = this.announcedTopics.get(topic);\n if (announcedTopic) {\n this.onDataReceived(announcedTopic, value, timestamp);\n }\n }\n\n if (this.connectionEstablished && this.serverTimeOffset_μs != null) {\n this.sendValueCore(publishedTopic, value, timestamp);\n }\n }\n\n /**\n * Returns current ping timeout.\n *\n * @returns A value in milliseconds or 0 if the timeout has not been set yet.\n */\n public get pingTimeoutMilliseconds() {\n return this.pingTimeout_ms;\n }\n\n /**\n * Sets ping timeout.\n *\n * @param value Timeout in milliseconds or `undefined` to use default protocol timeout value.\n */\n public setPingTimeoutMilliseconds(value: number | undefined) {\n Logger.Default.debug(\n value != null\n ? `[NT4Client] ping timeout is set to ${value.toFixed(0)}ms`\n : `[NT4Client] ping timeout is reset to protocol-based default`\n );\n\n this.pingTimeoutOverride_ms = value;\n\n // update actual timeout value\n if (value != null) {\n this.pingTimeout_ms = value;\n } else {\n this.pingTimeout_ms =\n this.dataChannel.protocol === NetworkSpecs.protocol41 ? NetworkSpecs.pingTimeout41 : NetworkSpecs.pingPeriod40;\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/NT4Spec.ts","../src/NT4Subscription.ts","../src/NT4Topic.ts","../src/NT4WebSocketAsync.ts","../src/NT4Client.ts"],"sourcesContent":["import type { SubscriptionOptions, TopicProperties } from \"./NT4Types\";\n\n/**\n * Network constants.\n */\nexport const NetworkSpecs = {\n /** NT4 port for unsecure (ws://) connections */\n portUnsecure: 5810,\n\n /** NT4 port for secure (wss://) connections */\n portSecure: 5811,\n\n /** Subprotocol v4.1 */\n protocol41: \"v4.1.networktables.first.wpi.edu\",\n\n /** Subprotocol v4.0 */\n protocol40: \"networktables.first.wpi.edu\",\n\n /** Subprotocol RTT */\n protocolRTT: \"rtt.networktables.first.wpi.edu\",\n\n /** v4.0 ping period in milliseconds */\n pingPeriod40: 1000,\n\n /** v4.0 ping timeout in milliseconds */\n pingTimeout40: 3000,\n\n /** v4.1 ping period in milliseconds */\n pingPeriod41: 250,\n\n /** v4.1 ping timeout in milliseconds */\n pingTimeout41: 1000,\n};\n\n/**\n * Supported data types mapped to protocol identifiers.\n *\n * https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#supported-data-types\n */\nexport const DataTypeCodes = {\n boolean: 0,\n double: 1,\n int: 2,\n float: 3,\n string: 4,\n json: 4,\n raw: 5,\n rpc: 5,\n msgpack: 5,\n protobuf: 5,\n \"boolean[]\": 16,\n \"double[]\": 17,\n \"int[]\": 18,\n \"float[]\": 19,\n \"string[]\": 20,\n} as const;\n\nexport type DataType = keyof typeof DataTypeCodes;\n\n/**\n * Publish Request Message (`publish`)\n *\n * https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#publish-request-message-publish\n */\nexport type PublishRequestMessage = {\n /**\n * **Publish name**\n *\n * The topic name being published.\n */\n name: string;\n\n /**\n * **Published UID**\n *\n * A client-generated unique identifier for this publisher. Use the same UID later to unpublish.\n * This is also the identifier that the client will use in MessagePack messages for this topic.\n */\n pubuid: number;\n\n /**\n * **Type of data**\n *\n * The requested data type (as a string).\n * If the topic is newly created (e.g. there are no other publishers) this sets the value type.\n * If the topic was previously published, this is ignored.\n */\n type: string;\n\n /**\n * **Properties**\n *\n * Initial topic properties.\n * If the topic is newly created (e.g. there are no other publishers) this sets the topic properties.\n * If the topic was previously published, this is ignored.\n */\n properties: TopicProperties;\n};\n\n/**\n * Publish Release Message (`unpublish`)\n *\n * https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#publish-release-message-unpublish\n */\nexport type PublishReleaseMessage = {\n /**\n * **Publisher UID**\n *\n * The same unique identifier passed to the `publish` message.\n */\n pubuid: number;\n};\n\n/**\n * Set Properties Message (`setproperties`)\n *\n * https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#set-properties-message-setproperties\n */\nexport type SetPropertiesMessage = {\n /**\n * **Topic name**\n */\n name: string;\n\n /**\n * **Properties to update**\n */\n update: TopicProperties;\n};\n\n/**\n * Subscribe Message (`subscribe`)\n *\n * https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#subscribe-message-subscribe\n */\nexport type SubscribeMessage = {\n /**\n * **Array of topic names or prefixes**\n *\n * One or more topic names or prefixes (if the `prefix` option is true) to start receiving messages for.\n */\n topics: Array<string>;\n\n /**\n * **Subscription UID**\n *\n * A client-generated unique identifier for this subscription. Use the same UID later to unsubscribe.\n */\n subuid: number;\n\n /**\n * **Options**\n */\n options: SubscriptionOptions;\n};\n\n/**\n * Unsubscribe Message (`unsubscribe`)\n *\n * https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#unsubscribe-message-unsubscribe\n */\nexport type UnsubscribeMessage = {\n /**\n * **Subscription UID**\n *\n * The same unique identifier passed to the `subscribe` message.\n */\n subuid: number;\n};\n\n/**\n * Topic Announcement Message (`announce`)\n *\n * https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#topic-announcement-message-announce\n */\nexport type TopicAnnouncementMessage = {\n /**\n * **Topic name**\n */\n name: string;\n\n /**\n * **Topic ID**\n *\n * The identifier that the server will use in MessagePack messages for this topic.\n */\n id: number;\n\n /**\n * **Data type**\n *\n * The data type for the topic (as a string).\n */\n type: string;\n\n /**\n * **Publisher UID**\n *\n * If this message was sent in response to a publish message,\n * the Publisher UID provided in that message. Otherwise absent.\n */\n pubuid?: number;\n\n /**\n * **Properties**\n */\n properties: TopicProperties;\n};\n\n/**\n * Topic Removed Message (`unannounce`)\n *\n * https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#topic-removed-message-unannounce\n */\nexport type TopicRemovedMessage = {\n /**\n * **Topic name**\n */\n name: string;\n\n /**\n * **Topic ID**\n *\n * The identifier that the server was using for value updates.\n */\n id: number;\n};\n\n/**\n * Properties Update Message (`properties`)\n *\n * https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#properties-update-message-properties\n */\nexport type PropertiesUpdateMessage = {\n /**\n * **Topic name**\n */\n name: string;\n\n /**\n * **Acknowledgement**\n *\n * `True` if this message is in response to a `setproperties` message from the same client.\n * Otherwise absent.\n */\n ack: boolean;\n\n /**\n * **Properties to set**\n */\n update: TopicProperties;\n};\n\n/**\n * Supported text data frames.\n *\n * https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#text-data-frames\n */\nexport type TextDataFrame =\n | {\n method: \"publish\";\n params: PublishRequestMessage;\n }\n | {\n method: \"unpublish\";\n params: PublishReleaseMessage;\n }\n | {\n method: \"setproperties\";\n params: SetPropertiesMessage;\n }\n | {\n method: \"subscribe\";\n params: SubscribeMessage;\n }\n | {\n method: \"unsubscribe\";\n params: UnsubscribeMessage;\n }\n | {\n method: \"announce\";\n params: TopicAnnouncementMessage;\n }\n | {\n method: \"unannounce\";\n params: TopicRemovedMessage;\n }\n | {\n method: \"properties\";\n params: PropertiesUpdateMessage;\n };\n","import type { SubscriptionOptions } from \"./NT4Types\";\n\n/** Contains information about NT4 subscription. */\nexport class NT4Subscription {\n /** Subscription UID. */\n public readonly uid: number;\n\n /** Subscription options. */\n public readonly options: SubscriptionOptions;\n\n /** Subscribed topic names or prefixes. */\n public readonly topics: Set<string>;\n\n constructor(uid: number, topics: Iterable<string>, options?: SubscriptionOptions) {\n this.uid = uid;\n this.topics = new Set(typeof topics === \"string\" ? [topics] : topics);\n this.options = options ?? {};\n }\n}\n","import { DataTypeCodes } from \"./NT4Spec\";\n\nimport type { DataType } from \"./NT4Spec\";\nimport type { TopicProperties } from \"./NT4Types\";\n\n/** Contains information about NT4 topic. */\nexport class NT4Topic {\n /** Topic UID. */\n public readonly uid: number;\n\n /** Topic name. */\n public readonly name: string;\n\n /** Topic data type. */\n public readonly type: string;\n\n /** Topic properties. */\n public readonly properties: TopicProperties;\n\n /** Publisher UID. */\n public pubuid?: number;\n\n /** Last retained value. */\n public retainedValue?: unknown;\n\n /** Timestamp of {@link retainedValue}. */\n public retainedTimestamp?: number;\n\n constructor(uid: number, name: string, type: string, properties?: TopicProperties, pubuid?: number) {\n this.uid = uid;\n this.name = name;\n this.type = type;\n this.properties = properties ?? {};\n this.pubuid = pubuid;\n this.typeCode = DataTypeCodes[type as DataType] ?? 5; // defaults to binary\n }\n\n /** Merges properties with this topic. */\n public mergeProperties(properties: TopicProperties) {\n Object.entries(properties).forEach(([key, value]) => {\n if (value === null) {\n delete this.properties[key];\n } else {\n this.properties[key] = value;\n }\n });\n }\n\n /** Updates retained value if the incoming timestamp is newer (greater-than or equal-to). */\n public updateRetainedValue(value: unknown, timestamp: number) {\n if (this.retainedTimestamp == null || timestamp >= this.retainedTimestamp) {\n this.retainedValue = value;\n this.retainedTimestamp = timestamp;\n }\n }\n\n /** Removes retained value except for \"weak\" default value. */\n public removeRetainedValue() {\n if (this.retainedTimestamp != 0) {\n this.retainedValue = undefined;\n this.retainedTimestamp = undefined;\n }\n }\n\n /** Resets retained value timestamp except for \"weak\" default value. */\n public resetRetainedTimestamp() {\n if (this.retainedTimestamp != null && this.retainedTimestamp > 1) {\n this.retainedTimestamp = 1;\n }\n }\n\n /** Determines whether topic can accept the specified value. */\n public canAccept(value: unknown) {\n switch (this.typeCode) {\n // primitives\n case DataTypeCodes.boolean:\n return typeof value === \"boolean\";\n case DataTypeCodes.double:\n case DataTypeCodes.float:\n return typeof value === \"number\";\n case DataTypeCodes.int:\n return typeof value === \"number\" && Number.isInteger(value);\n case DataTypeCodes.string:\n return typeof value === \"string\";\n\n // arrays of primitives\n case DataTypeCodes[\"boolean[]\"]:\n return Array.isArray(value) && value.every((v) => typeof v === \"boolean\");\n case DataTypeCodes[\"double[]\"]:\n case DataTypeCodes[\"float[]\"]:\n return Array.isArray(value) && value.every((v) => typeof v === \"number\");\n case DataTypeCodes[\"int[]\"]:\n return Array.isArray(value) && value.every((v) => typeof v === \"number\" && Number.isInteger(v));\n case DataTypeCodes[\"string[]\"]:\n return Array.isArray(value) && value.every((v) => typeof v === \"string\");\n\n case DataTypeCodes.json:\n return value !== null && (typeof value === \"object\" || typeof value === \"string\");\n\n default:\n return true;\n }\n }\n\n /** Topic type code. For internal use only. */\n public readonly typeCode: number;\n}\n","import { NetworkSpecs } from \"./NT4Spec\";\n\nimport type { TextDataFrame } from \"./NT4Spec\";\n\n// version 4.1 -- v4.1.networktables.first.wpi.edu\n// version 4.0 -- networktables.first.wpi.edu\nconst PROTOCOL_DATA = [NetworkSpecs.protocol41, NetworkSpecs.protocol40];\n\n// https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#rtt-subprotocol\nconst PROTOCOL_RTT = [NetworkSpecs.protocolRTT];\n\n/**\n * A wrapper for `WebSocket` that implements async `connect` and `close` methods.\n *\n * This class manages the lifecycle of the raw websocket internally, creating and\n * destroying the websocket as necessary when establishing and closing the connection.\n */\nexport class NT4WebSocketAsync {\n private url: string;\n private rtt: boolean;\n private socket: WebSocket | null = null;\n private onMessage?: (_: MessageEvent) => void;\n private onClose?: (_: CloseEvent) => void;\n\n /**\n * Creates a `WebSocket` wrapper to be used for NT4 communication channel.\n * This constructor does not initiate the connection, see `connect`.\n *\n * @param address server network address\n * @param port sever port\n * @param clientId client identifier\n * @param options connection options\n */\n constructor(\n address: string,\n port: number,\n clientId: string,\n options: {\n /** Callback invoked when socket receives message. */\n onMessage?: (ev: MessageEvent) => void;\n /** Callback invoked when socket closes. */\n onClose?: (ev: CloseEvent) => void;\n /** `true` to use secure protocol/port; `false` to use unsecure protocol/port (default). */\n secure?: boolean;\n /** `true` to create RTT subprotocol channel; `false` to create default data channel (default). */\n rtt?: boolean;\n }\n ) {\n const schema = options.secure ? \"wss\" : \"ws\";\n\n this.url = `${schema}://${address}:${port}/nt/${clientId}`;\n this.rtt = options.rtt ?? false;\n this.onMessage = options.onMessage;\n this.onClose = options.onClose;\n }\n\n private onMessageLocal(ev: MessageEvent, source: WebSocket) {\n if (this.onMessage && this.socket === source) {\n this.onMessage(ev);\n }\n }\n\n private onCloseLocal(ev: CloseEvent, source: WebSocket) {\n if (this.onClose && this.socket === source) {\n this.onClose(ev);\n }\n }\n\n /** Returns `true` if the socket is in the connected state. */\n public get connected() {\n return this.socket != null && this.socket.readyState === WebSocket.OPEN;\n }\n\n /** Returns `true` if the socket is in the connecting state. */\n public get connecting() {\n return this.socket != null && this.socket.readyState === WebSocket.CONNECTING;\n }\n\n /** Returns the subprotocol negotiated with the server. */\n public get protocol() {\n return this.socket?.protocol;\n }\n\n /** Sends text data frame (JSON serialized). */\n public sendText(data: TextDataFrame | Array<TextDataFrame>) {\n if (this.socket && this.socket.readyState === WebSocket.OPEN) {\n this.socket.send(JSON.stringify(Array.isArray(data) ? data : [data]));\n }\n }\n\n /** Sends binary data frame. */\n public sendBinary(data: Uint8Array) {\n if (this.socket && this.socket.readyState === WebSocket.OPEN) {\n this.socket.send(data);\n }\n }\n\n /**\n * Asynchronously terminates the connection.\n *\n * @param code optional code\n * @param reason optional reason\n */\n public async close(code?: number, reason?: string): Promise<void> {\n if (this.socket) {\n const socket = this.socket;\n this.socket = null;\n\n // websocket is closing asynchronously, we need to wait\n // for the corresponding onclose to fire:\n // attach another listener here and promisify\n return new Promise((resolve) => {\n socket.addEventListener(\"close\", () => {\n resolve();\n });\n\n socket.close(code, reason);\n });\n }\n\n return Promise.resolve();\n }\n\n /** Asynchronously initiates the connection. */\n public async connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n const socket = new WebSocket(this.url, this.rtt ? PROTOCOL_RTT : PROTOCOL_DATA);\n\n socket.binaryType = \"arraybuffer\";\n\n this.socket = socket;\n\n socket.addEventListener(\"message\", (_) => this.onMessageLocal(_, socket));\n socket.addEventListener(\"close\", (_) => this.onCloseLocal(_, socket));\n\n socket.onopen = () => {\n resolve();\n };\n\n socket.onerror = (event) => {\n reject(event);\n };\n });\n }\n}\n","import { Decoder, Encoder } from \"@msgpack/msgpack\";\n\nimport { Logger } from \"@2702rebels/logger\";\n\nimport { DataTypeCodes, NetworkSpecs } from \"./NT4Spec\";\nimport { NT4Subscription } from \"./NT4Subscription\";\nimport { NT4Topic } from \"./NT4Topic\";\nimport { NT4WebSocketAsync } from \"./NT4WebSocketAsync\";\n\nimport type { DataType, TextDataFrame } from \"./NT4Spec\";\nimport type { SubscriptionOptions, TopicProperties } from \"./NT4Types\";\n\nexport type NT4ClientConnectionState = \"disconnected\" | \"connecting\" | \"connected\";\n\nexport type NT4ClientOptions = {\n /** Indicates that secure channel should be used. */\n secure?: boolean;\n\n /**\n * Invoked when the client connects.\n */\n onConnect?: () => void;\n\n /**\n * Invoked when the client disconnects.\n */\n onDisconnect?: () => void;\n\n /**\n * Invoked when new topic is announced by the server.\n *\n * @param topic topic announced\n */\n onTopicAnnounced?: (topic: NT4Topic) => void;\n\n /**\n * Invoked when a topic is removed by the server.\n *\n * @param topic topic removed\n */\n onTopicRemoved?: (topic: NT4Topic) => void;\n\n /**\n * Invoked when a topic properties are updated by the server.\n *\n * @param topic topic updated\n */\n onTopicUpdated?: (topic: NT4Topic) => void;\n\n /**\n * Invoked when the topic receives new data.\n *\n * @param topic topic receiving data\n * @param value value received\n * @param timestamp server timestamp in microseconds\n */\n onDataReceived?: (topic: NT4Topic, value: unknown, timestamp: number) => void;\n\n /**\n * Retry policy that accepts the current number of connection attempts\n * and returns the timeout in milliseconds or -1 to abort.\n *\n * @param attempts number of subsequent connection attempts\n * @returns timeout in milliseconds or -1 to abort\n */\n retryPolicy?: (attempts: number) => number;\n\n /**\n * Overrides default timeout for ping-pong packets.\n *\n * Defaults:\n * - NT 4.0: 3000 milliseconds\n * - NT 4.1: 1000 milliseconds\n */\n pingTimeoutMilliseconds?: number;\n};\n\n/** Determines whether the specified predicate callback returns true for any element of a map. */\nfunction some<K, V>(map: Map<K, V>, predicate: (entry: V) => boolean) {\n for (const v of map.values()) {\n if (predicate(v)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Implements a NetworkTable4 client that is responsible for establishing and maintaining a connection.\n */\nexport class NT4Client {\n private readonly port: number;\n private readonly clientId: string;\n private readonly dataChannel: NT4WebSocketAsync;\n private readonly rttChannel: NT4WebSocketAsync;\n private readonly decoder = new Decoder();\n private readonly encoder = new Encoder();\n private readonly onConnect?: NT4ClientOptions[\"onConnect\"];\n private readonly onDisconnect?: NT4ClientOptions[\"onDisconnect\"];\n private readonly onTopicAnnounced?: NT4ClientOptions[\"onTopicAnnounced\"];\n private readonly onTopicRemoved?: NT4ClientOptions[\"onTopicRemoved\"];\n private readonly onTopicUpdated?: NT4ClientOptions[\"onTopicUpdated\"];\n private readonly onDataReceived?: NT4ClientOptions[\"onDataReceived\"];\n private readonly retryPolicy: Required<NT4ClientOptions>[\"retryPolicy\"];\n private pingTimeoutOverride_ms?: NT4ClientOptions[\"pingTimeoutMilliseconds\"];\n\n private subscriptions = new Map<number, NT4Subscription>();\n private publishedTopics = new Map<string, NT4Topic>();\n private announcedTopics = new Map<string, NT4Topic>();\n private announcedTopicsIndex = new Map<number, NT4Topic>();\n\n private connectionEstablished = false;\n private connectionInitiated = false;\n private connectionAttempts = 0;\n private connectionState: NT4ClientConnectionState = \"disconnected\";\n private networkLatency_μs: number | null = null;\n private serverTimeOffset_μs: number | null = null;\n private pingTimeout_ms = 0;\n private pongTimestamp_μs = 0;\n private pingIntervalId: ReturnType<typeof setInterval> | null = null;\n private pongIntervalId: ReturnType<typeof setInterval> | null = null;\n\n /** Truncated at 15 seconds exponential backoff retry policy starting at 1 second. */\n public static defaultRetryPolicy(attempts: number) {\n return Math.round(Math.min(15, Math.max(1, 1.2 ** Math.min(15, attempts))) * 1000);\n }\n\n /** Cleans up closed data channel. */\n private cleanup() {\n Logger.Default.debug(`[NT4Client] cleaning up`);\n\n if (this.pingIntervalId) {\n clearInterval(this.pingIntervalId);\n this.pingIntervalId = null;\n }\n\n if (this.pongIntervalId) {\n clearInterval(this.pongIntervalId);\n this.pongIntervalId = null;\n }\n\n this.rttChannel.close();\n\n if (this.connectionEstablished && this.onDisconnect) {\n this.onDisconnect();\n }\n\n this.connectionEstablished = false;\n this.connectionState = \"disconnected\";\n\n this.announcedTopics.clear();\n this.announcedTopicsIndex.clear();\n\n for (const topic of this.publishedTopics.values()) {\n topic.resetRetainedTimestamp();\n }\n\n this.pongTimestamp_μs = 0;\n this.serverTimeOffset_μs = null;\n this.networkLatency_μs = null;\n }\n\n /** Attempts to establish a connection, continuously retrying using policy. */\n private async connectWithRetry() {\n this.connectionState = \"connecting\";\n\n // check for cancellation after each await, since disconnect could have been called\n // while we were awaiting and therefore we should abort here or below\n if (!this.connectionInitiated) {\n Logger.Default.debug(`[NT4Client] pending cancellation, aborting...`);\n return;\n }\n\n // probe remote endpoint status (liveness check)\n let response: Response | null = null;\n try {\n response = await fetch(`http://${this.serverAddress}:${this.port}`, {\n signal: AbortSignal.timeout(250),\n });\n } catch {\n // swallow exception here; we are checking for response status below\n }\n\n if (!this.connectionInitiated) {\n Logger.Default.debug(`[NT4Client] pending cancellation, aborting...`);\n return;\n }\n\n // if endpoint seems to be alive try to establish websocket connection,\n // otherwise, wait and retry again\n if (response && response.ok) {\n try {\n Logger.Default.debug(`[NT4Client] opening channel to ${this.serverAddress}:${this.port}`);\n await this.dataChannel.connect();\n\n // reset connection attempts once the data channel is successfully connected\n this.connectionAttempts = 0;\n\n if (!this.connectionInitiated) {\n Logger.Default.debug(`[NT4Client] pending cancellation, aborting...`);\n return;\n }\n\n this.connectionEstablished = true;\n this.connectionState = \"connected\";\n\n Logger.Default.debug(\n `[NT4Client] connected to ${this.serverAddress}:${this.port}, subprotocol = ${this.dataChannel.protocol}`\n );\n\n let pingPeriod_ms = NetworkSpecs.pingPeriod40;\n this.pingTimeout_ms = this.pingTimeoutOverride_ms ?? NetworkSpecs.pingTimeout40;\n\n if (this.dataChannel.protocol === NetworkSpecs.protocol41) {\n Logger.Default.debug(`[NT4Client] server speaks v4.1, creating separate RTT channel`);\n await this.rttChannel.connect();\n\n if (!this.connectionInitiated) {\n Logger.Default.debug(`[NT4Client] pending cancellation, aborting...`);\n return;\n }\n\n pingPeriod_ms = NetworkSpecs.pingPeriod41;\n this.pingTimeout_ms = this.pingTimeoutOverride_ms ?? NetworkSpecs.pingTimeout41;\n }\n\n Logger.Default.debug(`[NT4Client] sending timestamp and re-establishing subscriptions`);\n\n // send timestamp to establish RTT\n this.sendRTT();\n\n // and start periodic ping\n this.pongTimestamp_μs = 0;\n this.pingIntervalId = setInterval(() => this.sendRTT(), pingPeriod_ms);\n this.pongIntervalId = setInterval(() => this.checkTimeout(), pingPeriod_ms);\n\n // restore client subscriptions\n for (const subscription of this.subscriptions.values()) {\n this.subscribeCore(subscription);\n }\n\n // restore client publishers\n // retained values won't be sent until clock is synchronized\n for (const topic of this.publishedTopics.values()) {\n this.publishCore(topic);\n }\n\n if (this.onConnect) {\n this.onConnect();\n }\n\n return;\n } catch (exception) {\n Logger.Default.error(`[NT4Client] failed to open channel`, exception);\n\n await this.rttChannel.close();\n await this.dataChannel.close();\n }\n }\n\n // retry logic\n this.connectionAttempts++;\n const timeout = this.retryPolicy(this.connectionAttempts);\n\n if (timeout < 0) {\n Logger.Default.debug(`[NT4Client] aborting per retry policy after ${this.connectionAttempts} attempts`);\n this.disconnect();\n return;\n }\n\n Logger.Default.debug(`[NT4Client] retrying connection in ${timeout.toFixed(0)}ms`);\n setTimeout(() => this.connectWithRetry(), timeout);\n }\n\n /** Invoked once the client-server clock is synchronized. */\n private onClockSynchronized() {\n Logger.Default.debug(`[NT4Client] clock synchronized, offset = ${this.serverTimeOffset_μs}`);\n\n for (const topic of this.publishedTopics.values()) {\n if (topic.retainedTimestamp != null && topic.retainedValue != null) {\n this.sendValueCore(\n topic,\n topic.retainedValue,\n topic.retainedTimestamp === 1 ? (this.serverTimeMicroseconds() ?? 1) : topic.retainedTimestamp\n );\n }\n }\n }\n\n /** Consumes received data frame. */\n private processDataFrame(data: string | BufferSource) {\n if (typeof data === \"string\") {\n let array: Array<TextDataFrame> | undefined;\n try {\n array = JSON.parse(data);\n if (!Array.isArray(array)) {\n Logger.Default.error(`[NT4Client] unexpected text frame, JSON must be an array, frame ignored`);\n return;\n }\n } catch (exception) {\n Logger.Default.error(`[NT4Client] unexpected text frame, JSON is malformed, frame ignored`, exception);\n return;\n }\n\n array.forEach((d) => {\n if (typeof d !== \"object\" || !(\"method\" in d) || !(\"params\" in d)) {\n Logger.Default.error(`[NT4Client] malformed message in text frame, message ignored`);\n return;\n }\n\n const { method, params } = d;\n if (typeof method !== \"string\" || typeof params !== \"object\") {\n Logger.Default.error(`[NT4Client] malformed message in text frame, message ignored`);\n return;\n }\n\n switch (method) {\n case \"announce\": {\n const topic = new NT4Topic(params.id, params.name, params.type, params.properties, params.pubuid);\n this.announcedTopics.set(topic.name, topic);\n this.announcedTopicsIndex.set(topic.uid, topic);\n\n if (this.onTopicAnnounced) {\n this.onTopicAnnounced(topic);\n }\n return;\n }\n\n case \"unannounce\": {\n const topic = this.announcedTopics.get(params.name);\n\n // ignore topics that we haven't seen being announced\n if (topic) {\n this.publishedTopics.get(topic.name)?.removeRetainedValue();\n this.announcedTopics.delete(topic.name);\n this.announcedTopicsIndex.delete(topic.uid);\n\n if (this.onTopicRemoved) {\n this.onTopicRemoved(topic);\n }\n }\n return;\n }\n\n case \"properties\": {\n const topic = this.announcedTopics.get(params.name);\n // ignore topics that we haven't seen being announced\n if (topic) {\n // https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#properties-update-message-properties\n // The client shall handle the update value as follows.\n // If a property is not included in the update map, its value is not changed.\n // If a property is provided in the update map with a value of null, the property is deleted.\n topic.mergeProperties(params.update);\n\n if (this.onTopicUpdated) {\n this.onTopicUpdated(topic);\n }\n }\n return;\n }\n\n default:\n Logger.Default.error(`[NT4Client] unexpected method '${method}' in text frame message, message ignored`);\n return;\n }\n });\n } else {\n for (const d of this.decoder.decodeMulti(data)) {\n const [topicId, timestamp, typeCode, value] = d as [number, number, number, unknown];\n\n // sanity checks\n if (typeof topicId !== \"number\") {\n Logger.Default.error(`[NT4Client] unexpected non-numeric topic identifier, frame ignored`);\n return;\n }\n\n if (typeof timestamp !== \"number\") {\n Logger.Default.error(`[NT4Client] unexpected non-numeric timestamp, frame ignored`);\n return;\n }\n\n if (typeof typeCode !== \"number\") {\n Logger.Default.error(`[NT4Client] unexpected non-numeric typeCode, frame ignored`);\n return;\n }\n\n if (topicId === -1) {\n // RTT measurement\n if (typeof value !== \"number\") {\n Logger.Default.error(`[NT4Client] unexpected non-numeric value in RTT frame, message ignored`);\n continue;\n }\n\n const clientTime_μs = this.getClientTimestamp_μs();\n const latency_μs = (clientTime_μs - value) / 2.0; // network latency is estimated as half of RTT\n\n // if the RTT is less than that from previous measurements, the client shall use\n // the timestamp in the message plus ½ the RTT as the server time equivalent to\n // the current local time\n if (this.networkLatency_μs == null || latency_μs < this.networkLatency_μs) {\n const clockSynchronized = this.serverTimeOffset_μs == null;\n\n // server timestamp is in microseconds\n this.serverTimeOffset_μs = Math.round(timestamp + latency_μs - clientTime_μs);\n\n if (clockSynchronized) {\n this.onClockSynchronized();\n }\n }\n\n this.networkLatency_μs = latency_μs;\n\n // update pong timestamp (used for checking timeouts)\n this.pongTimestamp_μs = clientTime_μs;\n } else if (topicId > 0) {\n const topic = this.announcedTopicsIndex.get(topicId);\n if (topic == null) {\n Logger.Default.error(`[NT4Client] unexpected topic identifier ${topicId}, message ignored`);\n continue;\n } else if (this.onDataReceived) {\n // update locally retained value on topics published by the client\n this.publishedTopics.get(topic.name)?.updateRetainedValue(value, timestamp);\n this.onDataReceived(topic, value, timestamp);\n }\n } else {\n Logger.Default.error(`[NT4Client] invalid topic identifier ${topicId}, message ignored`);\n continue;\n }\n }\n }\n }\n\n /** Handles general data frame received. */\n private onChannelMessage = (ev: MessageEvent) => {\n this.processDataFrame(ev.data);\n };\n\n /** Handles RTT data frame received. */\n private onChannelMessageRTT = (ev: MessageEvent) => {\n if (typeof ev.data === \"string\") {\n Logger.Default.debug(`[NT4Client] unexpected text data frame on RTT channel, frame ignored`);\n return;\n }\n\n this.processDataFrame(ev.data);\n };\n\n /** Handles channel closing for whatever reason. */\n private onChannelClose = (ev: CloseEvent) => {\n Logger.Default.debug(`[NT4Client] channel closed`, ev);\n\n this.cleanup();\n this.connectWithRetry();\n };\n\n /** Returns current timestamp in microseconds. This is NOT wall clock time. */\n private getClientTimestamp_μs() {\n // monotonically increasing, microsecond precision\n return Math.round(performance.now() * 1000);\n }\n\n /** Sends data frame to publish the topic. */\n private publishCore(topic: NT4Topic) {\n this.dataChannel.sendText({\n method: \"publish\",\n params: {\n pubuid: topic.uid,\n name: topic.name,\n type: topic.type,\n properties: topic.properties,\n },\n });\n }\n\n /** Sends data frame to unpublish the topic. */\n private unpublishCore(topic: NT4Topic) {\n this.dataChannel.sendText({\n method: \"unpublish\",\n params: {\n pubuid: topic.uid,\n },\n });\n }\n\n /** Sends data frame to set topic properties. */\n private setPropertiesCore(name: string, properties: TopicProperties) {\n this.dataChannel.sendText({\n method: \"setproperties\",\n params: {\n name: name,\n update: properties,\n },\n });\n }\n\n /** Sends data frame to subscribe the subscription. */\n private subscribeCore(subscription: NT4Subscription) {\n this.dataChannel.sendText({\n method: \"subscribe\",\n params: {\n subuid: subscription.uid,\n topics: Array.from(subscription.topics),\n options: subscription.options,\n },\n });\n }\n\n /** Sends data frame to unsubscribe the subscription. */\n private unsubscribeCore(subscription: NT4Subscription) {\n this.dataChannel.sendText({\n method: \"unsubscribe\",\n params: {\n subuid: subscription.uid,\n },\n });\n }\n\n /** Sends data frame to update topic value. */\n private sendValueCore(topic: NT4Topic, value: unknown, timestamp: number) {\n const frame = this.encoder.encode([topic.uid, timestamp, topic.typeCode, value]);\n this.dataChannel.sendBinary(frame);\n }\n\n /**\n * Sends RTT measurement binary frame.\n * Uses RTT channel if available, falls back to data channel in NT4.0.\n */\n private sendRTT() {\n // https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#timestamps\n // The topic ID of -1 is reserved for timestamp communication.\n // Clients shall periodically (e.g. every few seconds) send, in a manner that\n // minimizes transmission delays, a MessagePack message with ID of -1, a timestamp of 0,\n // and an implementation-selected type and data value (typically int or float 64)\n // containing its (the client’s) current local time\n const timestamp = this.getClientTimestamp_μs();\n const frame = this.encoder.encode([-1, 0, DataTypeCodes.double, timestamp]);\n (this.rttChannel ?? this.dataChannel).sendBinary(frame);\n }\n\n /**\n * Determines whether connection times out if no message has been received\n * within the timeout interval since the last pong.\n */\n private async checkTimeout() {\n if (!this.connectionEstablished || this.pongTimestamp_μs === 0) {\n return;\n }\n\n const delta_μs = this.getClientTimestamp_μs() - this.pongTimestamp_μs;\n if (delta_μs > this.pingTimeout_ms * 1000) {\n Logger.Default.debug(\n `[NT4Client] no data received in the last ${(delta_μs / 1000).toFixed(0)}ms, connection timed out`\n );\n\n await this.dataChannel.close(4001, \"timeout\");\n this.cleanup();\n this.connectWithRetry();\n }\n }\n\n /** Generates random UID. */\n private generateUID() {\n return Math.floor(Math.random() * 0x7fffffff);\n }\n\n /** Generates random safe UID avoiding possible clashes. */\n private generateSafeUID(has: (id: number) => boolean) {\n let uid: number;\n for (uid = this.generateUID(); has(uid); uid = this.generateUID());\n return uid;\n }\n\n /** Determines whether published topic with the specified identifier exists. */\n private readonly hasPublishedTopicWithId = (id: number) => some(this.publishedTopics, (_) => _.uid === id);\n\n /** Determines whether subscription with the specified identifier exists. */\n private readonly hasSubscriptionWithId = (id: number) => this.subscriptions.has(id);\n\n /**\n * Constructs an instance of NT4Client without initiating a connection.\n *\n * @param serverAddress NT4 server network address\n * @param clientId this client identifier (cannot contain '@')\n * @param options options\n */\n constructor(serverAddress: string, clientId: string, options: NT4ClientOptions) {\n // https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#websockets-protocol-configuration\n // ...\n // The client name does not need to be unique; multiple connections to the same name are allowed;\n // the server shall ensure the name is unique (for the purposes of meta-topics) by appending a '@'\n // and a unique number (if necessary). To support this, the name provided by the client should not\n // contain an embedded '@'. Clients should provide a way to specify the resource name (in particular,\n // the client name portion).\n if (clientId.includes(\"@\")) {\n throw new Error(\"Client identifier must not contain '@' character\");\n }\n\n this.serverAddress = serverAddress;\n this.port = options.secure ? NetworkSpecs.portSecure : NetworkSpecs.portUnsecure;\n this.clientId = clientId;\n this.onConnect = options.onConnect;\n this.onDisconnect = options.onDisconnect;\n this.onTopicAnnounced = options.onTopicAnnounced;\n this.onTopicRemoved = options.onTopicRemoved;\n this.onTopicUpdated = options.onTopicUpdated;\n this.onDataReceived = options.onDataReceived;\n this.retryPolicy = options.retryPolicy ?? NT4Client.defaultRetryPolicy;\n this.pingTimeoutOverride_ms = options.pingTimeoutMilliseconds;\n\n // data messages channel\n this.dataChannel = new NT4WebSocketAsync(this.serverAddress, this.port, this.clientId, {\n secure: options.secure,\n onMessage: this.onChannelMessage,\n onClose: this.onChannelClose,\n });\n\n // RTT messages channel (only used in NT4.1)\n this.rttChannel = new NT4WebSocketAsync(this.serverAddress, this.port, this.clientId, {\n rtt: true,\n secure: options.secure,\n onMessage: this.onChannelMessageRTT,\n });\n }\n\n /**\n * Returns NT4 server network address the client is using.\n */\n public readonly serverAddress: string;\n\n /**\n * Returns current connection state.\n */\n public get state() {\n return this.connectionState;\n }\n\n /**\n * Returns current network latency in microseconds if known.\n */\n public get networkLatencyMicroseconds() {\n return this.networkLatency_μs;\n }\n\n /**\n * Returns server time in microseconds that is current or\n * based on the specified client (local) time.\n *\n * Returns `null` if the server time offset is not established.\n */\n public serverTimeMicroseconds(clientTimeMicroseconds?: number) {\n return this.serverTimeOffset_μs != null\n ? Math.round(\n (clientTimeMicroseconds != null ? clientTimeMicroseconds : this.getClientTimestamp_μs()) +\n this.serverTimeOffset_μs\n )\n : null;\n }\n\n /**\n * Initiates the connection.\n * Once established the connection will be automatically kept alive by reconnecting.\n */\n public connect() {\n if (this.connectionInitiated) {\n return;\n }\n\n Logger.Default.debug(`[NT4Client] connection initiated`);\n this.connectionInitiated = true;\n this.connectWithRetry();\n }\n\n /**\n * Terminates the connection.\n * The client can be reconnected manually by calling {@link connect} again.\n */\n public disconnect() {\n if (!this.connectionInitiated) {\n return;\n }\n\n Logger.Default.debug(`[NT4Client] connection cancelled`);\n this.connectionInitiated = false;\n this.dataChannel.close();\n this.rttChannel.close();\n }\n\n /**\n * Creates a new subscription.\n *\n * The subscription can be created regardless of connectivity state.\n * All client subscriptions are automatically restored once connection\n * has been established. This is also true for reconnects following\n * loss of connectivity or deliberate disconnects.\n *\n * Caller can {@link unsubscribe} by passing the return value of this call or\n * invoke {@link unsubscribeAll} to remove all existing client subscriptions.\n *\n * @param topics topics or prefixes to include in the subscription\n * @param options subscription options\n * @returns A subscription instance\n */\n public subscribe(topics: Iterable<string>, options: SubscriptionOptions) {\n const uid = this.generateSafeUID(this.hasSubscriptionWithId);\n const subscription = new NT4Subscription(uid, topics, options);\n this.subscriptions.set(subscription.uid, subscription);\n\n if (this.connectionEstablished) {\n this.subscribeCore(subscription);\n }\n\n return subscription;\n }\n\n /**\n * Unsubscribes an existing subscription.\n *\n * @param arg subscription or subscription identifier\n * @returns `true` if the subscription was successfully unsubscribed, `false` if the subscription was not found\n */\n public unsubscribe(arg: NT4Subscription | number) {\n const subscription = typeof arg === \"number\" ? this.subscriptions.get(arg) : arg;\n if (!subscription) {\n return false;\n }\n\n this.subscriptions.delete(subscription.uid);\n if (this.connectionEstablished) {\n this.unsubscribeCore(subscription);\n }\n\n return true;\n }\n\n /**\n * Unsubscribes all existing subscriptions.\n */\n public unsubscribeAll() {\n for (const subscription of this.subscriptions.values()) {\n this.unsubscribe(subscription);\n }\n }\n\n /**\n * Publishes a topic with the specified name and type.\n *\n * This method registers this client as a publisher for the topic.\n * It should be called before {@link setValue} or {@link setValueAt}\n * can be used.\n *\n * Publishing a topic on a disconnected client is allowed.\n * Client will automatically register such publishers once the connection\n * has been established and send default or retained value as appropriate.\n *\n * @param topic topic name\n * @param type data type\n * @param properties properties\n */\n public publishTopic(topic: string, type: DataType | string, properties?: TopicProperties) {\n let publishedTopic = this.publishedTopics.get(topic);\n if (publishedTopic) {\n return publishedTopic;\n }\n\n const uid = this.generateSafeUID(this.hasPublishedTopicWithId);\n publishedTopic = new NT4Topic(uid, topic, type, properties, uid);\n this.publishedTopics.set(publishedTopic.name, publishedTopic);\n\n if (this.connectionEstablished) {\n this.publishCore(publishedTopic);\n }\n\n return publishedTopic;\n }\n\n /**\n * Un-publishes previously published topic.\n *\n * @param topic topic name\n * @returns `true` if the topic was successfully unpublished, `false` if the topic was not published\n */\n public unpublishTopic(topic: string) {\n const publishedTopic = this.publishedTopics.get(topic);\n if (!publishedTopic) {\n return false;\n }\n\n this.publishedTopics.delete(publishedTopic.name);\n if (this.connectionEstablished) {\n this.unpublishCore(publishedTopic);\n }\n\n return true;\n }\n\n /**\n * Determines whether client already publishes the topic.\n *\n * @param topic topic name\n */\n public isTopicPublished(topic: string) {\n return this.publishedTopics.has(topic);\n }\n\n /**\n * Sets properties for the specified topic.\n *\n * @param topic topic name\n * @param properties new properties\n */\n public setTopicProperties(topic: string, properties: TopicProperties) {\n const publishedTopic = this.publishedTopics.get(topic);\n if (publishedTopic) {\n publishedTopic.mergeProperties(properties);\n }\n\n const announcedTopic = this.announcedTopics.get(topic);\n if (announcedTopic) {\n announcedTopic.mergeProperties(properties);\n }\n\n if (this.connectionEstablished) {\n this.setPropertiesCore(topic, properties);\n }\n }\n\n /**\n * Sets persistent flag for the topic.\n *\n * @param topic topic name\n * @param value value to set\n */\n public setTopicPersistent(topic: string, value: boolean) {\n this.setTopicProperties(topic, { persistent: value });\n }\n\n /**\n * Sets retained flag for the topic.\n *\n * @param topic topic name\n * @param value value to set\n */\n public setTopicRetained(topic: string, value: boolean) {\n this.setTopicProperties(topic, { retained: value });\n }\n\n /**\n * Sends a value to the published topic using the current time.\n *\n * This client must publish the topic with {@link publishTopic} first.\n *\n * @param topic topic name\n * @param value value to send\n */\n public setValue(topic: string, value: unknown) {\n this.setValueAt(topic, value, this.serverTimeMicroseconds() ?? 1);\n }\n\n /**\n * Sends a default (\"weak\") value to the published topic.\n *\n * This client must publish the topic with {@link publishTopic} first.\n *\n * @param topic topic name\n * @param value value to send\n */\n public setDefaultValue(topic: string, value: unknown) {\n this.setValueAt(topic, value, 0);\n }\n\n /**\n * Sends a value to the published topic using the specified timestamp.\n *\n * This client must publish the topic with {@link publishTopic} first.\n *\n * @param topic topic name\n * @param value value to send\n * @param timestamp timestamp\n */\n public setValueAt(topic: string, value: unknown, timestamp: number) {\n const publishedTopic = this.publishedTopics.get(topic);\n if (!publishedTopic) {\n throw new Error(\"Topic must be published first\");\n }\n\n if (!publishedTopic.canAccept(value)) {\n throw new Error(\"Value is not compatible with topic underlying type\");\n }\n\n // timestamps in messages always use the server time base,\n // if the offset has not been established, the client retains\n // the last value and will send it once connection is restored\n publishedTopic.updateRetainedValue(value, timestamp);\n\n // update locally for announced topics\n if (this.onDataReceived) {\n const announcedTopic = this.announcedTopics.get(topic);\n if (announcedTopic) {\n this.onDataReceived(announcedTopic, value, timestamp);\n }\n }\n\n if (this.connectionEstablished && this.serverTimeOffset_μs != null) {\n this.sendValueCore(publishedTopic, value, timestamp);\n }\n }\n\n /**\n * Returns current ping timeout.\n *\n * @returns A value in milliseconds or 0 if the timeout has not been set yet.\n */\n public get pingTimeoutMilliseconds() {\n return this.pingTimeout_ms;\n }\n\n /**\n * Sets ping timeout.\n *\n * @param value Timeout in milliseconds or `undefined` to use default protocol timeout value.\n */\n public setPingTimeoutMilliseconds(value: number | undefined) {\n Logger.Default.debug(\n value != null\n ? `[NT4Client] ping timeout is set to ${value.toFixed(0)}ms`\n : `[NT4Client] ping timeout is reset to protocol-based default`\n );\n\n this.pingTimeoutOverride_ms = value;\n\n // update actual timeout value\n if (value != null) {\n this.pingTimeout_ms = value;\n } else {\n this.pingTimeout_ms =\n this.dataChannel.protocol === NetworkSpecs.protocol41 ? NetworkSpecs.pingTimeout41 : NetworkSpecs.pingPeriod40;\n }\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@2702rebels/ntcore",
|
|
3
3
|
"description": "Typescript implementation of NetworkTables protocol",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.2",
|
|
5
5
|
"license": "BSD-3-Clause",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"exports": {
|
|
@@ -37,9 +37,9 @@
|
|
|
37
37
|
"@2702rebels/logger": "1.0.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@types/node": "
|
|
41
|
-
"@2702rebels/
|
|
42
|
-
"@2702rebels/
|
|
40
|
+
"@types/node": "26.6.2",
|
|
41
|
+
"@2702rebels/typescript-config": "2.0.0",
|
|
42
|
+
"@2702rebels/eslint-config": "2.0.0"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
45
|
"clean": "rimraf .turbo && rimraf node_modules && rimraf dist",
|