Skip to content

Commit

Permalink
feat: add prefix topics & fix: 2025 robot communication (#158)
Browse files Browse the repository at this point in the history
* feat: add prefix topics

* feat: update readme

* feat(topic)!: remove immediateNotify

BREAKING CHANGE: removes immediateNotify from topic subscription

* chore: update changelog

* chore: bump version

* fix(readme): subscribe fn params type

* fix: integer overflow on UID generator

* fix: remove RTT and only heartbeat on v4.0

* fix: start publish timeout after robot connected

* feat: add example client

* feat: count sub/pub ids from 0

* chore: update deps
  • Loading branch information
cjlawson02 authored Jan 21, 2025
1 parent 383a361 commit b276b31
Show file tree
Hide file tree
Showing 54 changed files with 1,732 additions and 1,055 deletions.
78 changes: 73 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# ntcore-ts-client

A TypeScript library for communication over [WPILib's NetworkTables 4.0 protocol](https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc).
A TypeScript library for communication over [WPILib's NetworkTables 4.1 protocol](https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc).

## Features

- NodeJS and DOM support
- Togglable auto-reconnect
- Callbacks for new data on subscriptions
- Callbacks for connection listeners
- Wildcard prefix listeners for multiple topics
- Retrying for messages queued during a connection loss
- On-the-fly server switching with resubscribing and republishing
- Generic types for Topics
Expand Down Expand Up @@ -84,8 +85,7 @@ Once a topic has been created, it can be used as a subscriber:

```typescript
subscribe(
callback: (_: T | null) => void,
immediateNotify = false,
callback: (value: T | null, params: AnnounceMessageParams) => void,
options: SubscribeOptions = {},
id?: number,
save = true
Expand All @@ -112,7 +112,12 @@ const gyroTopic = ntcore.createTopic<number>('/MyTable/Gyro', NetworkTablesTypeI
// Subscribe and immediately call the callback with the current value
gyroTopic.subscribe((value) => {
console.log(`Got Gyro Value: ${value}`);
}, true);
});

// Or you can use the topic's announce parameters to get more info, like the topic ID
gyroTopic.subscribe((value, params) => {
console.log(`Got Gyro Value: ${value} at from topic id ${params.id}`);
});
```
Or a publisher for an auto mode:
Expand All @@ -133,7 +138,70 @@ await autoModeTopic.publish();
autoModeTopic.setValue('25 Ball Auto and Climb');
```
### More info
### Subscribing to Multiple Topics
You can also subscribe to multiple topics by using a "wildcard" through creating a prefix topic.
For example, here's a subscription for an Accelerometer with topics `/MyTable/Accelerometer/X`, `/MyTable/Accelerometer/Y`, and `/MyTable/Accelerometer/Z`:
```typescript
import { NetworkTables } from 'ntcore-ts-client';

// Get or create the NT client instance
const ntcore = NetworkTables.getInstanceByTeam(973);

// Create the accelerator topic
const accelerometerTopic = ntcore.createPrefixTopic('/MyTable/Accelerometer/');

let x, y, z;

// Subscribe to all topics under the prefix /MyTable/Accelerometer/
accelerometerTopic.subscribe((value, params) => {
console.log(`Got Accelerometer Value: ${value} from topic ${params.name}`); // i.e. Got Accelerometer Value: 9.81 from topic /MyTable/Accelerometer/Y

// You can also use the topic name to determine which value to set
if (params.name.endsWith('X')) {
x = value;
} else if (params.name.endsWith('Y')) {
y = value;
} else if (params.name.endsWith('Z')) {
z = value;
}

// Since there can be many types in subtopics,
// you can use the type information for other checks...
if (params.type === 'int') {
console.warn('Hmm... the accelerometer seems low precision');
} else if (params.type === 'double') {
console.log('The accelerometer is high precision');
}
});

// x, y, and z will be updated as new values come in
```
### Subscribing to All Topics
You can also subscribe to all topics by doing the above, but with a prefix of `/`.
For example, here's a subscription for all topics:
```typescript
import { NetworkTables } from 'ntcore-ts-client';

// Get or create the NT client instance
const ntcore = NetworkTables.getInstanceByTeam(973);

// Create a prefix for all topics
const allTopics = ntcore.createPrefixTopic('/');

// Subscribe to all topics
allTopics.subscribe((value, params) => {
console.log(`Got Value: ${value} from topic ${params.name}`);
});
```
### More Info
The API for Topics is much more exhaustive than this quick example. Feel free to view the docs at [https://ntcore.chrislawson.dev](https://ntcore.chrislawson.dev).
Expand Down
20 changes: 20 additions & 0 deletions apps/example-client/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"extends": ["../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {
"no-console": "off"
}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}
61 changes: 61 additions & 0 deletions apps/example-client/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{
"name": "example-client",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/example-client/src",
"projectType": "application",
"tags": [],
"targets": {
"build": {
"executor": "@nx/esbuild:esbuild",
"outputs": ["{options.outputPath}"],
"defaultConfiguration": "production",
"options": {
"platform": "node",
"outputPath": "dist/apps/example-client",
"format": ["cjs"],
"bundle": false,
"main": "apps/example-client/src/main.ts",
"tsConfig": "apps/example-client/tsconfig.app.json",
"assets": ["apps/example-client/src/assets"],
"generatePackageJson": true,
"esbuildOptions": {
"sourcemap": true,
"outExtension": {
".js": ".js"
}
}
},
"configurations": {
"development": {},
"production": {
"esbuildOptions": {
"sourcemap": false,
"outExtension": {
".js": ".js"
}
}
}
}
},
"serve": {
"executor": "@nx/js:node",
"defaultConfiguration": "development",
"dependsOn": ["build"],
"options": {
"buildTarget": "example-client:build",
"runBuildTargetDependencies": false
},
"configurations": {
"development": {
"buildTarget": "example-client:build:development"
},
"production": {
"buildTarget": "example-client:build:production"
}
}
},
"lint": {
"executor": "@nx/eslint:lint"
}
}
}
88 changes: 88 additions & 0 deletions apps/example-client/src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// eslint-disable-next-line @nx/enforce-module-boundaries
import { NetworkTables, NetworkTablesTypeInfos } from '../../../packages/ntcore-ts-client/src';

// Get or create the NT client instance
const ntcore = NetworkTables.getInstanceByURI('localhost');

// ------------------------------------------------ //
// Example of using a topic to subscribe to a value //
// ------------------------------------------------ //

// Create the gyro topic
const gyroTopic = ntcore.createTopic<number>('/MyTable/Gyro', NetworkTablesTypeInfos.kDouble);

// Subscribe and immediately call the callback with the current value
gyroTopic.subscribe((value) => {
console.log(`Got Gyro Value: ${value}`);
});

// Or you can use the topic's announce parameters to get more info, like the topic ID
gyroTopic.subscribe((value, params) => {
console.log(`Got Gyro Value: ${value} at from topic id ${params.id}`);
});

// ---------------------------------------------- //
// Example of using a topic to publish to a value //
// ---------------------------------------------

// Create the autoMode topic w/ a default return value of 'No Auto'
(async () => {
const autoModeTopic = ntcore.createTopic<string>('/MyTable/autoMode', NetworkTablesTypeInfos.kString, 'No Auto');

// Make us the publisher
await autoModeTopic.publish();

// Set a new value, this will error if we aren't the publisher!
autoModeTopic.setValue('25 Ball Auto and Climb');
})();

// --------------------------------------------------------------- //
// Example of using a prefix topic to subscribe to multiple topics //
// --------------------------------------------------------------- //

// Create the accelerator topic
const accelerometerTopic = ntcore.createPrefixTopic('/MyTable/Accelerometer/');

let x, y, z: any;

// Subscribe to all topics under the prefix /MyTable/Accelerometer/
accelerometerTopic.subscribe((value, params) => {
console.log(`Got Accelerometer Value: ${value} from topic ${params.name}`); // i.e. Got Accelerometer Value: 9.81 from topic /MyTable/Accelerometer/Y

// You can also use the topic name to determine which value to set
if (params.name.endsWith('X')) {
x = value;
} else if (params.name.endsWith('Y')) {
y = value;
} else if (params.name.endsWith('Z')) {
z = value;
}

// Since there can be THAT many different types in subtopics,
// you can use the type information for other checks...
if (params.type === 'int') {
console.warn('Hmm... the accelerometer seems low precision');
} else if (params.type === 'double') {
console.log('The accelerometer is high precision');

const typedX = x as number;
const typedY = y as number;
const typedZ = z as number;

console.log(`Latest update: X: ${typedX}, Y: ${typedY}, Z: ${typedZ}`);
}
});

// x, y, and z will be updated as new values come in

// ---------------------------------------------------------- //
// Example of using a prefix topic to subscribe to all topics //
// ---------------------------------------------------------- //

// Create a prefix for all topics
const allTopics = ntcore.createPrefixTopic('');

// Sub scribe to all topics
allTopics.subscribe((value, params) => {
console.log(`Got Value: ${value} from topic ${params.name}`);
});
9 changes: 9 additions & 0 deletions apps/example-client/tsconfig.app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "ESNext",
"types": ["node"]
},
"include": ["src/main.ts"]
}
13 changes: 13 additions & 0 deletions apps/example-client/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "../../tsconfig.base.json",
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.app.json"
}
],
"compilerOptions": {
"esModuleInterop": true
}
}
2 changes: 1 addition & 1 deletion docs/assets/search.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit b276b31

Please sign in to comment.