Refactor typescript message handler
This makes it so that, to register a handler on a given topic, you just
need to call a single function, rather than assembling the entire
flatbuffer request message by yourself.
Change-Id: I540f475d9650ab27efe715a05d7586719045aeed
diff --git a/aos/network/www/BUILD b/aos/network/www/BUILD
index 03e269d..8f5bd49 100644
--- a/aos/network/www/BUILD
+++ b/aos/network/www/BUILD
@@ -20,6 +20,7 @@
target_compatible_with = ["@platforms//os:linux"],
visibility = ["//visibility:public"],
deps = [
+ ":reflection_ts",
"//aos:configuration_ts_fbs",
"//aos/network:connect_ts_fbs",
"//aos/network:web_proxy_ts_fbs",
diff --git a/aos/network/www/config_handler.ts b/aos/network/www/config_handler.ts
index 81f61b4..df2d4f8 100644
--- a/aos/network/www/config_handler.ts
+++ b/aos/network/www/config_handler.ts
@@ -2,6 +2,8 @@
import {Connection} from 'org_frc971/aos/network/www/proxy';
import * as flatbuffers_builder from 'org_frc971/external/com_github_google_flatbuffers/ts/builder';
import * as web_proxy from 'org_frc971/aos/network/web_proxy_generated';
+import {Parser, Table} from './reflection'
+import {ByteBuffer} from 'org_frc971/external/com_github_google_flatbuffers/ts/byte-buffer';
import Configuration = configuration.aos.Configuration;
@@ -62,35 +64,27 @@
handleChange() {
const toggles = this.root_div.getElementsByTagName('input');
- const builder =
- new flatbuffers_builder.Builder(512) as unknown as flatbuffers.Builder;
-
- const channels: flatbuffers.Offset[] = [];
for (const toggle of toggles) {
if (!toggle.checked) {
continue;
}
const index = toggle.getAttribute('data-index');
const channel = this.config.channels(Number(index));
- const namefb = builder.createString(channel.name());
- const typefb = builder.createString(channel.type());
- Channel.startChannel(builder);
- Channel.addName(builder, namefb);
- Channel.addType(builder, typefb);
- const channelfb = Channel.endChannel(builder);
- ChannelRequest.startChannelRequest(builder);
- ChannelRequest.addChannel(builder, channelfb);
- ChannelRequest.addMethod(builder, TransferMethod.SUBSAMPLE);
- channels.push(ChannelRequest.endChannelRequest(builder));
+ this.connection.addHandler(
+ channel.name(), channel.type(), (data, time) => {
+ const config = this.connection.getConfig();
+ let schema = null;
+ for (let ii = 0; ii < config.channelsLength(); ++ii) {
+ if (config.channels(ii).type() === channel.type()) {
+ schema = config.channels(ii).schema();
+ }
+ }
+ const parser = new Parser(schema);
+ console.log(
+ parser.toObject(Table.getRootTable(new ByteBuffer(data))));
+ });
}
- const channelsfb =
- SubscriberRequest.createChannelsToTransferVector(builder, channels);
- SubscriberRequest.startSubscriberRequest(builder);
- SubscriberRequest.addChannelsToTransfer(builder, channelsfb);
- const request = SubscriberRequest.endSubscriberRequest(builder);
- builder.finish(request);
- this.connection.sendConnectMessage(builder);
}
toggleConfig() {
diff --git a/aos/network/www/graph_main.ts b/aos/network/www/graph_main.ts
index 9135cf4..2548e1d 100644
--- a/aos/network/www/graph_main.ts
+++ b/aos/network/www/graph_main.ts
@@ -121,6 +121,7 @@
};
let reportParser = null;
+let startTime = null;
conn.addConfigHandler((config: Configuration) => {
// Locate the timing report schema so that we can read the received messages.
@@ -135,44 +136,22 @@
}
reportParser = new Parser(reportSchema);
- // Subscribe to the timing report message.
- const builder =
- new flatbuffers_builder.Builder(512) as unknown as flatbuffers.Builder;
- const channels: flatbuffers.Offset[] = [];
- for (const channel of [timingReport]) {
- const nameFb = builder.createString(channel.name);
- const typeFb = builder.createString(channel.type);
- Channel.startChannel(builder);
- Channel.addName(builder, nameFb);
- Channel.addType(builder, typeFb);
- const channelFb = Channel.endChannel(builder);
- ChannelRequest.startChannelRequest(builder);
- ChannelRequest.addChannel(builder, channelFb);
- ChannelRequest.addMethod(builder, TransferMethod.EVERYTHING_WITH_HISTORY);
- channels.push(ChannelRequest.endChannelRequest(builder));
- }
+ conn.addReliableHandler(
+ timingReport.name, timingReport.type,
+ (data: Uint8Array, time: number) => {
+ if (startTime === null) {
+ startTime = time;
+ }
+ time = time - startTime;
+ const table = Table.getRootTable(new ByteBuffer(data));
- const channelsFb = SubscriberRequest.createChannelsToTransferVector(builder, channels);
- SubscriberRequest.startSubscriberRequest(builder);
- SubscriberRequest.addChannelsToTransfer(builder, channelsFb);
- const connect = SubscriberRequest.endSubscriberRequest(builder);
- builder.finish(connect);
- conn.sendConnectMessage(builder);
-});
-
-let startTime = null;
-conn.addHandler(timingReport.type, (data: Uint8Array, time: number) => {
- if (startTime === null) {
- startTime = time;
- }
- time = time - startTime;
- const table = Table.getRootTable(new ByteBuffer(data));
-
- const timer = reportParser.readVectorOfTables(table, "timers")[0];
- handlerLines.addPoints(
- reportParser, reportParser.readTable(timer, 'handler_time'), time);
- latencyLines.addPoints(
- reportParser, reportParser.readTable(timer, 'wakeup_latency'), time);
+ const timer = reportParser.readVectorOfTables(table, 'timers')[0];
+ handlerLines.addPoints(
+ reportParser, reportParser.readTable(timer, 'handler_time'), time);
+ latencyLines.addPoints(
+ reportParser, reportParser.readTable(timer, 'wakeup_latency'),
+ time);
+ });
});
// Set up and draw the benchmarking plot
diff --git a/aos/network/www/main.ts b/aos/network/www/main.ts
index 1840ffb..79a39d8 100644
--- a/aos/network/www/main.ts
+++ b/aos/network/www/main.ts
@@ -7,4 +7,4 @@
PingHandler.SetupDom();
-conn.addHandler(PingHandler.GetId(), PingHandler.HandlePing);
+conn.addHandler('/aos', PingHandler.GetId(), PingHandler.HandlePing);
diff --git a/aos/network/www/proxy.ts b/aos/network/www/proxy.ts
index 5660cef..4fbba85 100644
--- a/aos/network/www/proxy.ts
+++ b/aos/network/www/proxy.ts
@@ -3,6 +3,7 @@
import {Builder} from 'org_frc971/external/com_github_google_flatbuffers/ts/builder';
import {ByteBuffer} from 'org_frc971/external/com_github_google_flatbuffers/ts/byte-buffer';
+import ChannelFb = configuration.aos.Channel;
import Configuration = configuration.aos.Configuration;
import MessageHeader = web_proxy.aos.web_proxy.MessageHeader;
import WebSocketIce = web_proxy.aos.web_proxy.WebSocketIce;
@@ -10,6 +11,9 @@
import Payload = web_proxy.aos.web_proxy.Payload;
import WebSocketSdp = web_proxy.aos.web_proxy.WebSocketSdp;
import SdpType = web_proxy.aos.web_proxy.SdpType;
+import SubscriberRequest = web_proxy.aos.web_proxy.SubscriberRequest;
+import ChannelRequestFb = web_proxy.aos.web_proxy.ChannelRequest;
+import TransferMethod = web_proxy.aos.web_proxy.TransferMethod;
// There is one handler for each DataChannel, it maintains the state of
// multi-part messages and delegates to a callback when the message is fully
@@ -53,6 +57,19 @@
}
}
+class Channel {
+ constructor(public readonly name: string, public readonly type: string) {}
+ key(): string {
+ return this.name + "/" + this.type;
+ }
+}
+
+class ChannelRequest {
+ constructor(
+ public readonly channel: Channel,
+ public readonly transferMethod: TransferMethod) {}
+}
+
// Analogous to the Connection class in //aos/network/web_proxy.h. Because most
// of the apis are native in JS, it is much simpler.
export class Connection {
@@ -69,6 +86,8 @@
new Map<string, (data: Uint8Array, sentTime: number) => void>();
private readonly handlers = new Set<Handler>();
+ private subscribedChannels: ChannelRequest[] = [];
+
constructor() {
const server = location.host;
this.webSocketUrl = `ws://${server}/ws`;
@@ -79,12 +98,62 @@
}
/**
- * Add a handler for a specific message type. Until we need to handle
- * different channel names with the same type differently, this is good
- * enough.
+ * Add a handler for a specific message type, with reliable delivery of all
+ * messages.
*/
- addHandler(id: string, handler: (data: Uint8Array, sentTime: number) => void): void {
- this.handlerFuncs.set(id, handler);
+ addReliableHandler(
+ name: string, type: string,
+ handler: (data: Uint8Array, sentTime: number) => void): void {
+ this.addHandlerImpl(
+ name, type, TransferMethod.EVERYTHING_WITH_HISTORY, handler);
+ }
+
+ /**
+ * Add a handler for a specific message type.
+ */
+ addHandler(
+ name: string, type: string,
+ handler: (data: Uint8Array, sentTime: number) => void): void {
+ this.addHandlerImpl(name, type, TransferMethod.SUBSAMPLE, handler);
+ }
+
+ addHandlerImpl(
+ name: string, type: string, method: TransferMethod,
+ handler: (data: Uint8Array, sentTime: number) => void): void {
+ const channel = new Channel(name, type);
+ const request = new ChannelRequest(channel, method);
+ this.handlerFuncs.set(channel.key(), handler);
+ this.subscribeToChannel(request);
+ }
+
+ subscribeToChannel(channel: ChannelRequest): void {
+ this.subscribedChannels.push(channel);
+ if (this.configInternal === null) {
+ throw new Error(
+ 'Must call subscribeToChannel after we\'ve received the config.');
+ }
+ const builder = new Builder(512) as unknown as flatbuffers.Builder;
+ const channels: flatbuffers.Offset[] = [];
+ for (const channel of this.subscribedChannels) {
+ const nameFb = builder.createString(channel.channel.name);
+ const typeFb = builder.createString(channel.channel.type);
+ ChannelFb.startChannel(builder);
+ ChannelFb.addName(builder, nameFb);
+ ChannelFb.addType(builder, typeFb);
+ const channelFb = ChannelFb.endChannel(builder);
+ ChannelRequestFb.startChannelRequest(builder);
+ ChannelRequestFb.addChannel(builder, channelFb);
+ ChannelRequestFb.addMethod(builder, channel.transferMethod);
+ channels.push(ChannelRequestFb.endChannelRequest(builder));
+ }
+
+ const channelsFb =
+ SubscriberRequest.createChannelsToTransferVector(builder, channels);
+ SubscriberRequest.startSubscriberRequest(builder);
+ SubscriberRequest.addChannelsToTransfer(builder, channelsFb);
+ const connect = SubscriberRequest.endSubscriberRequest(builder);
+ builder.finish(connect);
+ this.sendConnectMessage(builder);
}
connect(): void {
@@ -114,8 +183,7 @@
onDataChannel(ev: RTCDataChannelEvent): void {
const channel = ev.channel;
const name = channel.label;
- const channelType = name.split('/').pop();
- const handlerFunc = this.handlerFuncs.get(channelType);
+ const handlerFunc = this.handlerFuncs.get(name);
this.handlers.add(new Handler(handlerFunc, channel));
}
diff --git a/y2020/www/camera_main.ts b/y2020/www/camera_main.ts
index 8c7ae55..58e5bed 100644
--- a/y2020/www/camera_main.ts
+++ b/y2020/www/camera_main.ts
@@ -1,9 +1,11 @@
import {Connection} from 'org_frc971/aos/network/www/proxy';
import {ImageHandler} from './image_handler';
+import {ConfigHandler} from 'org_frc971/aos/network/www/config_handler';
const conn = new Connection();
conn.connect();
const iHandler = new ImageHandler(conn);
+const configHandler = new ConfigHandler(conn);
diff --git a/y2020/www/field_handler.ts b/y2020/www/field_handler.ts
index d3bafa2..b2daea1 100644
--- a/y2020/www/field_handler.ts
+++ b/y2020/www/field_handler.ts
@@ -59,37 +59,6 @@
const ROBOT_WIDTH = 28 * IN_TO_M;
const ROBOT_LENGTH = 30 * IN_TO_M;
-/**
- * All the messages that are required to display camera information on the field.
- * Messages not readable on the server node are ignored.
- */
-const REQUIRED_CHANNELS = [
- {
- name: '/pi1/camera',
- type: 'frc971.vision.sift.ImageMatchResult',
- },
- {
- name: '/pi2/camera',
- type: 'frc971.vision.sift.ImageMatchResult',
- },
- {
- name: '/pi3/camera',
- type: 'frc971.vision.sift.ImageMatchResult',
- },
- {
- name: '/pi4/camera',
- type: 'frc971.vision.sift.ImageMatchResult',
- },
- {
- name: '/pi5/camera',
- type: 'frc971.vision.sift.ImageMatchResult',
- },
- {
- name: '/drivetrain',
- type: 'frc971.control_loops.drivetrain.Status',
- },
-];
-
export class FieldHandler {
private canvas = document.createElement('canvas');
private imageMatchResult: ImageMatchResult|null = null;
@@ -99,16 +68,15 @@
document.body.appendChild(this.canvas);
this.connection.addConfigHandler(() => {
- this.sendConnect();
+ this.connection.addHandler(
+ '/camera', ImageMatchResult.getFullyQualifiedName(), (res) => {
+ this.handleImageMatchResult(res);
+ });
+ this.connection.addHandler(
+ '/drivetrain', DrivetrainStatus.getFullyQualifiedName(), (data) => {
+ this.handleDrivetrainStatus(data);
+ });
});
- this.connection.addHandler(
- ImageMatchResult.getFullyQualifiedName(), (res) => {
- this.handleImageMatchResult(res);
- });
- this.connection.addHandler(
- DrivetrainStatus.getFullyQualifiedName(), (data) => {
- this.handleDrivetrainStatus(data);
- });
}
private handleImageMatchResult(data: Uint8Array): void {
@@ -123,32 +91,6 @@
fbBuffer as unknown as flatbuffers.ByteBuffer);
}
- private sendConnect(): void {
- const builder =
- new flatbuffers_builder.Builder(512) as unknown as flatbuffers.Builder;
- const channels: flatbuffers.Offset[] = [];
- for (const channel of REQUIRED_CHANNELS) {
- const nameFb = builder.createString(channel.name);
- const typeFb = builder.createString(channel.type);
- Channel.startChannel(builder);
- Channel.addName(builder, nameFb);
- Channel.addType(builder, typeFb);
- const channelFb = Channel.endChannel(builder);
- ChannelRequest.startChannelRequest(builder);
- ChannelRequest.addChannel(builder, channelFb);
- ChannelRequest.addMethod(builder, TransferMethod.SUBSAMPLE);
- channels.push(ChannelRequest.endChannelRequest(builder));
- }
-
- const channelsFb =
- SubscriberRequest.createChannelsToTransferVector(builder, channels);
- SubscriberRequest.startSubscriberRequest(builder);
- SubscriberRequest.addChannelsToTransfer(builder, channelsFb);
- const connect = SubscriberRequest.endSubscriberRequest(builder);
- builder.finish(connect);
- this.connection.sendConnectMessage(builder);
- }
-
drawField(): void {
const MY_COLOR = 'red';
const OTHER_COLOR = 'blue';
diff --git a/y2020/www/image_handler.ts b/y2020/www/image_handler.ts
index 79ce74e..b254f2c 100644
--- a/y2020/www/image_handler.ts
+++ b/y2020/www/image_handler.ts
@@ -16,53 +16,6 @@
import ChannelRequest = web_proxy.aos.web_proxy.ChannelRequest;
import TransferMethod = web_proxy.aos.web_proxy.TransferMethod;
-/*
- * All the messages that are required to show an image with metadata.
- * Messages not readable on the server node are ignored.
- */
-const REQUIRED_CHANNELS = [
- {
- name: '/pi1/camera',
- type: CameraImage.getFullyQualifiedName(),
- },
- {
- name: '/pi2/camera',
- type: CameraImage.getFullyQualifiedName(),
- },
- {
- name: '/pi3/camera',
- type: CameraImage.getFullyQualifiedName(),
- },
- {
- name: '/pi4/camera',
- type: CameraImage.getFullyQualifiedName(),
- },
- {
- name: '/pi5/camera',
- type: CameraImage.getFullyQualifiedName(),
- },
- {
- name: '/pi1/camera/detailed',
- type: ImageMatchResult.getFullyQualifiedName(),
- },
- {
- name: '/pi2/camera/detailed',
- type: ImageMatchResult.getFullyQualifiedName(),
- },
- {
- name: '/pi3/camera/detailed',
- type: ImageMatchResult.getFullyQualifiedName(),
- },
- {
- name: '/pi4/camera/detailed',
- type: ImageMatchResult.getFullyQualifiedName(),
- },
- {
- name: '/pi5/camera/detailed',
- type: ImageMatchResult.getFullyQualifiedName(),
- },
-];
-
export class ImageHandler {
private canvas = document.createElement('canvas');
private select = document.createElement('select');
@@ -86,41 +39,15 @@
document.body.appendChild(this.canvas);
this.connection.addConfigHandler(() => {
- this.sendConnect();
+ this.connection.addHandler(
+ '/camera', ImageMatchResult.getFullyQualifiedName(), (data) => {
+ this.handleImageMetadata(data);
+ });
+ this.connection.addHandler(
+ '/camera', CameraImage.getFullyQualifiedName(), (data) => {
+ this.handleImage(data);
+ });
});
- this.connection.addHandler(
- ImageMatchResult.getFullyQualifiedName(), (data) => {
- this.handleImageMetadata(data);
- });
- this.connection.addHandler(CameraImage.getFullyQualifiedName(), (data) => {
- this.handleImage(data);
- });
- }
-
- private sendConnect(): void {
- const builder =
- new flatbuffers_builder.Builder(512) as unknown as flatbuffers.Builder;
- const channels: flatbuffers.Offset[] = [];
- for (const channel of REQUIRED_CHANNELS) {
- const nameFb = builder.createString(channel.name);
- const typeFb = builder.createString(channel.type);
- Channel.startChannel(builder);
- Channel.addName(builder, nameFb);
- Channel.addType(builder, typeFb);
- const channelFb = Channel.endChannel(builder);
- ChannelRequest.startChannelRequest(builder);
- ChannelRequest.addChannel(builder, channelFb);
- ChannelRequest.addMethod(builder, TransferMethod.SUBSAMPLE);
- channels.push(ChannelRequest.endChannelRequest(builder));
- }
-
- const channelsFb =
- SubscriberRequest.createChannelsToTransferVector(builder, channels);
- SubscriberRequest.startSubscriberRequest(builder);
- SubscriberRequest.addChannelsToTransfer(builder, channelsFb);
- const connect = SubscriberRequest.endSubscriberRequest(builder);
- builder.finish(connect);
- this.connection.sendConnectMessage(builder);
}
handleSelect(ev: Event) {