Add message bridge status to field webpage
Also reformat the file.
Change-Id: I87078ba61736851d8178b17ffabe69c94be90813
Signed-off-by: James Kuszmaul <jabukuszmaul@gmail.com>
diff --git a/aos/network/BUILD b/aos/network/BUILD
index f05a75e..88df785 100644
--- a/aos/network/BUILD
+++ b/aos/network/BUILD
@@ -65,11 +65,19 @@
name = "message_bridge_client_fbs",
srcs = ["message_bridge_client.fbs"],
gen_reflections = 1,
- includes = [
- ":message_bridge_server_fbs_includes",
- "//aos:configuration_fbs_includes",
- ],
target_compatible_with = ["@platforms//os:linux"],
+ deps = [
+ ":message_bridge_server_fbs",
+ "//aos:configuration_fbs",
+ ],
+)
+
+flatbuffer_ts_library(
+ name = "message_bridge_client_ts_fbs",
+ srcs = ["message_bridge_client.fbs"],
+ deps = [
+ ":message_bridge_server_ts_fbs",
+ ],
)
cc_static_flatbuffer(
@@ -83,10 +91,18 @@
name = "message_bridge_server_fbs",
srcs = ["message_bridge_server.fbs"],
gen_reflections = 1,
- includes = [
- "//aos:configuration_fbs_includes",
- ],
target_compatible_with = ["@platforms//os:linux"],
+ deps = [
+ "//aos:configuration_fbs",
+ ],
+)
+
+flatbuffer_ts_library(
+ name = "message_bridge_server_ts_fbs",
+ srcs = ["message_bridge_server.fbs"],
+ deps = [
+ "//aos:configuration_ts_fbs",
+ ],
)
cc_static_flatbuffer(
diff --git a/y2023/www/BUILD b/y2023/www/BUILD
index 63089b5..f2df646 100644
--- a/y2023/www/BUILD
+++ b/y2023/www/BUILD
@@ -28,6 +28,8 @@
target_compatible_with = ["@platforms//os:linux"],
deps = [
"//aos/network:connect_ts_fbs",
+ "//aos/network:message_bridge_client_ts_fbs",
+ "//aos/network:message_bridge_server_ts_fbs",
"//aos/network:web_proxy_ts_fbs",
"//aos/network/www:proxy",
"//frc971/control_loops:control_loops_ts_fbs",
diff --git a/y2023/www/field.html b/y2023/www/field.html
index a63c6f5..cc89bb9 100644
--- a/y2023/www/field.html
+++ b/y2023/www/field.html
@@ -36,66 +36,76 @@
</table>
<table>
- <tr>
- <th colspan="2">Superstructure</th>
- </tr>
- <tr>
- <td>End Effector State</td>
- <td id="end_effector_state"> NA </td>
- </tr>
- <tr>
- <td>Wrist</td>
- <td id="wrist"> NA </td>
- </tr>
- </table>
- <table>
- <tr>
- <th colspan="2">Game Piece</th>
- </tr>
- <tr>
- <td>Game Piece Held</td>
- <td id="game_piece"> NA </td>
- </tr>
- </table>
+ <tr>
+ <th colspan="2">Superstructure</th>
+ </tr>
+ <tr>
+ <td>End Effector State</td>
+ <td id="end_effector_state"> NA </td>
+ </tr>
+ <tr>
+ <td>Wrist</td>
+ <td id="wrist"> NA </td>
+ </tr>
+ </table>
+ <table>
+ <tr>
+ <th colspan="2">Game Piece</th>
+ </tr>
+ <tr>
+ <td>Game Piece Held</td>
+ <td id="game_piece"> NA </td>
+ </tr>
+ </table>
- <table>
- <tr>
- <th colspan="2">Arm</th>
- </tr>
- <tr>
- <td>State</td>
- <td id="arm_state"> NA </td>
- </tr>
- <tr>
- <td>X</td>
- <td id="arm_x"> NA </td>
- </tr>
- <tr>
- <td>Y</td>
- <td id="arm_y"> NA </td>
- </tr>
- <tr>
- <td>Circular Index</td>
- <td id="arm_circular_index"> NA </td>
- </tr>
- <tr>
- <td>Roll</td>
- <td id="arm_roll"> NA </td>
- </tr>
- <tr>
- <td>Proximal</td>
- <td id="arm_proximal"> NA </td>
- </tr>
- <tr>
- <td>Distal</td>
- <td id="arm_distal"> NA </td>
- </tr>
- </table>
- <h3>Zeroing Faults:</h3>
- <p id="zeroing_faults"> NA </p>
- </div>
+ <table>
+ <tr>
+ <th colspan="2">Arm</th>
+ </tr>
+ <tr>
+ <td>State</td>
+ <td id="arm_state"> NA </td>
+ </tr>
+ <tr>
+ <td>X</td>
+ <td id="arm_x"> NA </td>
+ </tr>
+ <tr>
+ <td>Y</td>
+ <td id="arm_y"> NA </td>
+ </tr>
+ <tr>
+ <td>Circular Index</td>
+ <td id="arm_circular_index"> NA </td>
+ </tr>
+ <tr>
+ <td>Roll</td>
+ <td id="arm_roll"> NA </td>
+ </tr>
+ <tr>
+ <td>Proximal</td>
+ <td id="arm_proximal"> NA </td>
+ </tr>
+ <tr>
+ <td>Distal</td>
+ <td id="arm_distal"> NA </td>
+ </tr>
+ </table>
+
+ <h3>Zeroing Faults:</h3>
+ <p id="zeroing_faults"> NA </p>
+ </div>
+ <div id="middle_readouts">
<div id="vision_readouts">
</div>
+ <div id="message_bridge_status">
+ <div>
+ <div>Node</div>
+ <div>Client</div>
+ <div>Server</div>
+ </div>
+ </div>
+ </div>
</body>
</html>
diff --git a/y2023/www/field_handler.ts b/y2023/www/field_handler.ts
index 65b9a20..1e08359 100644
--- a/y2023/www/field_handler.ts
+++ b/y2023/www/field_handler.ts
@@ -1,12 +1,15 @@
import {ByteBuffer} from 'flatbuffers';
+
+import {ClientStatistics} from '../../aos/network/message_bridge_client_generated'
+import {ServerStatistics, State as ConnectionState} from '../../aos/network/message_bridge_server_generated'
import {Connection} from '../../aos/network/www/proxy';
-import {LocalizerOutput} from '../../frc971/control_loops/drivetrain/localization/localizer_output_generated';
-import {RejectionReason} from '../localizer/status_generated';
-import {Status as DrivetrainStatus} from '../../frc971/control_loops/drivetrain/drivetrain_status_generated';
-import {Status as SuperstructureStatus, EndEffectorState, ArmState, ArmStatus} from '../control_loops/superstructure/superstructure_status_generated'
-import {Class} from '../vision/game_pieces_generated'
import {ZeroingError} from '../../frc971/control_loops/control_loops_generated';
-import {Visualization, TargetEstimateDebug} from '../localizer/visualization_generated';
+import {Status as DrivetrainStatus} from '../../frc971/control_loops/drivetrain/drivetrain_status_generated';
+import {LocalizerOutput} from '../../frc971/control_loops/drivetrain/localization/localizer_output_generated';
+import {ArmState, ArmStatus, EndEffectorState, Status as SuperstructureStatus} from '../control_loops/superstructure/superstructure_status_generated'
+import {RejectionReason} from '../localizer/status_generated';
+import {TargetEstimateDebug, Visualization} from '../localizer/visualization_generated';
+import {Class} from '../vision/game_pieces_generated'
import {FIELD_LENGTH, FIELD_WIDTH, FT_TO_M, IN_TO_M} from './constants';
@@ -36,34 +39,37 @@
private imagesAcceptedCounter: HTMLElement =
(document.getElementById('images_accepted') as HTMLElement);
private rejectionReasonCells: HTMLElement[] = [];
+ private messageBridgeDiv: HTMLElement =
+ (document.getElementById('message_bridge_status') as HTMLElement);
+ private clientStatuses = new Map<string, HTMLElement>();
+ private serverStatuses = new Map<string, HTMLElement>();
private fieldImage: HTMLImageElement = new Image();
private endEffectorState: HTMLElement =
- (document.getElementById('end_effector_state') as HTMLElement);
+ (document.getElementById('end_effector_state') as HTMLElement);
private wrist: HTMLElement =
- (document.getElementById('wrist') as HTMLElement);
+ (document.getElementById('wrist') as HTMLElement);
private armState: HTMLElement =
- (document.getElementById('arm_state') as HTMLElement);
+ (document.getElementById('arm_state') as HTMLElement);
private gamePiece: HTMLElement =
- (document.getElementById('game_piece') as HTMLElement);
- private armX: HTMLElement =
- (document.getElementById('arm_x') as HTMLElement);
- private armY: HTMLElement =
- (document.getElementById('arm_y') as HTMLElement);
+ (document.getElementById('game_piece') as HTMLElement);
+ private armX: HTMLElement = (document.getElementById('arm_x') as HTMLElement);
+ private armY: HTMLElement = (document.getElementById('arm_y') as HTMLElement);
private circularIndex: HTMLElement =
- (document.getElementById('arm_circular_index') as HTMLElement);
+ (document.getElementById('arm_circular_index') as HTMLElement);
private roll: HTMLElement =
- (document.getElementById('arm_roll') as HTMLElement);
+ (document.getElementById('arm_roll') as HTMLElement);
private proximal: HTMLElement =
- (document.getElementById('arm_proximal') as HTMLElement);
+ (document.getElementById('arm_proximal') as HTMLElement);
private distal: HTMLElement =
- (document.getElementById('arm_distal') as HTMLElement);
+ (document.getElementById('arm_distal') as HTMLElement);
private zeroingFaults: HTMLElement =
- (document.getElementById('zeroing_faults') as HTMLElement);_
+ (document.getElementById('zeroing_faults') as HTMLElement);
+ _
constructor(private readonly connection: Connection) {
(document.getElementById('field') as HTMLElement).appendChild(this.canvas);
- this.fieldImage.src = "2023.png";
+ this.fieldImage.src = '2023.png';
for (const value in RejectionReason) {
// Typescript generates an iterator that produces both numbers and
@@ -94,24 +100,28 @@
// matches.
for (const pi in PIS) {
this.connection.addReliableHandler(
- '/' + PIS[pi] + '/camera', "y2023.localizer.Visualization",
+ '/' + PIS[pi] + '/camera', 'y2023.localizer.Visualization',
(data) => {
this.handleLocalizerDebug(pi, data);
});
}
this.connection.addHandler(
- '/drivetrain', "frc971.control_loops.drivetrain.Status", (data) => {
+ '/drivetrain', 'frc971.control_loops.drivetrain.Status', (data) => {
this.handleDrivetrainStatus(data);
});
this.connection.addHandler(
- '/localizer', "frc971.controls.LocalizerOutput", (data) => {
+ '/localizer', 'frc971.controls.LocalizerOutput', (data) => {
this.handleLocalizerOutput(data);
});
- this.connection.addHandler(
- '/superstructure', "y2023.control_loops.superstructure.Status",
- (data) => {
- this.handleSuperstructureStatus(data)
- });
+ this.connection.addHandler(
+ '/superstructure', 'y2023.control_loops.superstructure.Status',
+ (data) => {this.handleSuperstructureStatus(data)});
+ this.connection.addHandler(
+ '/aos', 'aos.message_bridge.ServerStatistics',
+ (data) => {this.handleServerStatistics(data)});
+ this.connection.addHandler(
+ '/aos', 'aos.message_bridge.ClientStatistics',
+ (data) => {this.handleClientStatistics(data)});
});
}
@@ -149,8 +159,69 @@
}
private handleSuperstructureStatus(data: Uint8Array): void {
- const fbBuffer = new ByteBuffer(data);
- this.superstructureStatus = SuperstructureStatus.getRootAsStatus(fbBuffer);
+ const fbBuffer = new ByteBuffer(data);
+ this.superstructureStatus = SuperstructureStatus.getRootAsStatus(fbBuffer);
+ }
+
+ private populateNodeConnections(nodeName: string): void {
+ const row = document.createElement('div');
+ this.messageBridgeDiv.appendChild(row);
+ const nodeDiv = document.createElement('div');
+ nodeDiv.innerHTML = nodeName;
+ row.appendChild(nodeDiv);
+ const clientDiv = document.createElement('div');
+ clientDiv.innerHTML = 'N/A';
+ row.appendChild(clientDiv);
+ const serverDiv = document.createElement('div');
+ serverDiv.innerHTML = 'N/A';
+ row.appendChild(serverDiv);
+ this.serverStatuses.set(nodeName, serverDiv);
+ this.clientStatuses.set(nodeName, clientDiv);
+ }
+
+ private setCurrentNodeState(element: HTMLElement, state: ConnectionState):
+ void {
+ if (state === ConnectionState.CONNECTED) {
+ element.innerHTML = ConnectionState[state];
+ element.classList.remove('faulted');
+ element.classList.add('connected');
+ } else {
+ element.innerHTML = ConnectionState[state];
+ element.classList.remove('connected');
+ element.classList.add('faulted');
+ }
+ }
+
+ private handleServerStatistics(data: Uint8Array): void {
+ const fbBuffer = new ByteBuffer(data);
+ const serverStatistics =
+ ServerStatistics.getRootAsServerStatistics(fbBuffer);
+
+ for (let ii = 0; ii < serverStatistics.connectionsLength(); ++ii) {
+ const connection = serverStatistics.connections(ii);
+ const nodeName = connection.node().name();
+ if (!this.serverStatuses.has(nodeName)) {
+ this.populateNodeConnections(nodeName);
+ }
+ this.setCurrentNodeState(
+ this.serverStatuses.get(nodeName), connection.state());
+ }
+ }
+
+ private handleClientStatistics(data: Uint8Array): void {
+ const fbBuffer = new ByteBuffer(data);
+ const clientStatistics =
+ ClientStatistics.getRootAsClientStatistics(fbBuffer);
+
+ for (let ii = 0; ii < clientStatistics.connectionsLength(); ++ii) {
+ const connection = clientStatistics.connections(ii);
+ const nodeName = connection.node().name();
+ if (!this.clientStatuses.has(nodeName)) {
+ this.populateNodeConnections(nodeName);
+ }
+ this.setCurrentNodeState(
+ this.clientStatuses.get(nodeName), connection.state());
+ }
}
drawField(): void {
@@ -163,8 +234,8 @@
ctx.restore();
}
- drawCamera(
- x: number, y: number, theta: number, color: string = 'blue'): void {
+ drawCamera(x: number, y: number, theta: number, color: string = 'blue'):
+ void {
const ctx = this.canvas.getContext('2d');
ctx.save();
ctx.translate(x, y);
@@ -182,8 +253,8 @@
}
drawRobot(
- x: number, y: number, theta: number,
- color: string = 'blue', dashed: boolean = false): void {
+ x: number, y: number, theta: number, color: string = 'blue',
+ dashed: boolean = false): void {
const ctx = this.canvas.getContext('2d');
ctx.save();
ctx.translate(x, y);
@@ -216,22 +287,22 @@
}
setEstopped(div: HTMLElement): void {
- div.innerHTML = 'estopped';
- div.classList.add('faulted');
- div.classList.remove('zeroing');
- div.classList.remove('near');
+ div.innerHTML = 'estopped';
+ div.classList.add('faulted');
+ div.classList.remove('zeroing');
+ div.classList.remove('near');
}
setTargetValue(
- div: HTMLElement, target: number, val: number, tolerance: number): void {
- div.innerHTML = val.toFixed(4);
- div.classList.remove('faulted');
- div.classList.remove('zeroing');
- if (Math.abs(target - val) < tolerance) {
- div.classList.add('near');
- } else {
- div.classList.remove('near');
- }
+ div: HTMLElement, target: number, val: number, tolerance: number): void {
+ div.innerHTML = val.toFixed(4);
+ div.classList.remove('faulted');
+ div.classList.remove('zeroing');
+ if (Math.abs(target - val) < tolerance) {
+ div.classList.add('near');
+ } else {
+ div.classList.remove('near');
+ }
}
setValue(div: HTMLElement, val: number): void {
@@ -249,60 +320,92 @@
const now = Date.now() / 1000.0;
if (this.superstructureStatus) {
- this.endEffectorState.innerHTML =
- EndEffectorState[this.superstructureStatus.endEffectorState()];
- if (!this.superstructureStatus.wrist() ||
- !this.superstructureStatus.wrist().zeroed()) {
- this.setZeroing(this.wrist);
- } else if (this.superstructureStatus.wrist().estopped()) {
- this.setEstopped(this.wrist);
- } else {
- this.setTargetValue(
- this.wrist,
- this.superstructureStatus.wrist().unprofiledGoalPosition(),
- this.superstructureStatus.wrist().estimatorState().position(),
- 1e-3);
- }
- this.armState.innerHTML =
- ArmState[this.superstructureStatus.arm().state()];
- this.gamePiece.innerHTML =
- Class[this.superstructureStatus.gamePiece()];
- this.armX.innerHTML =
- this.superstructureStatus.arm().armX().toFixed(2);
- this.armY.innerHTML =
- this.superstructureStatus.arm().armY().toFixed(2);
- this.circularIndex.innerHTML =
- this.superstructureStatus.arm().armCircularIndex().toFixed(0);
- this.roll.innerHTML =
- this.superstructureStatus.arm().rollJointEstimatorState().position().toFixed(2);
- this.proximal.innerHTML =
- this.superstructureStatus.arm().proximalEstimatorState().position().toFixed(2);
- this.distal.innerHTML =
- this.superstructureStatus.arm().distalEstimatorState().position().toFixed(2);
- let zeroingErrors: string = "Roll Joint Errors:"+'<br/>';
- for (let i = 0; i < this.superstructureStatus.arm().rollJointEstimatorState().errors.length; i++) {
- zeroingErrors += ZeroingError[this.superstructureStatus.arm().rollJointEstimatorState().errors(i)]+'<br/>';
- }
- zeroingErrors += '<br/>'+"Proximal Joint Errors:"+'<br/>';
- for (let i = 0; i < this.superstructureStatus.arm().proximalEstimatorState().errors.length; i++) {
- zeroingErrors += ZeroingError[this.superstructureStatus.arm().proximalEstimatorState().errors(i)]+'<br/>';
- }
- zeroingErrors += '<br/>'+"Distal Joint Errors:"+'<br/>';
- for (let i = 0; i < this.superstructureStatus.arm().distalEstimatorState().errors.length; i++) {
- zeroingErrors += ZeroingError[this.superstructureStatus.arm().distalEstimatorState().errors(i)]+'<br/>';
- }
- zeroingErrors += '<br/>'+"Wrist Errors:"+'<br/>';
- for (let i = 0; i < this.superstructureStatus.wrist().estimatorState().errors.length; i++) {
- zeroingErrors += ZeroingError[this.superstructureStatus.wrist().estimatorState().errors(i)]+'<br/>';
- }
- this.zeroingFaults.innerHTML = zeroingErrors;
+ this.endEffectorState.innerHTML =
+ EndEffectorState[this.superstructureStatus.endEffectorState()];
+ if (!this.superstructureStatus.wrist() ||
+ !this.superstructureStatus.wrist().zeroed()) {
+ this.setZeroing(this.wrist);
+ } else if (this.superstructureStatus.wrist().estopped()) {
+ this.setEstopped(this.wrist);
+ } else {
+ this.setTargetValue(
+ this.wrist,
+ this.superstructureStatus.wrist().unprofiledGoalPosition(),
+ this.superstructureStatus.wrist().estimatorState().position(),
+ 1e-3);
+ }
+ this.armState.innerHTML =
+ ArmState[this.superstructureStatus.arm().state()];
+ this.gamePiece.innerHTML = Class[this.superstructureStatus.gamePiece()];
+ this.armX.innerHTML = this.superstructureStatus.arm().armX().toFixed(2);
+ this.armY.innerHTML = this.superstructureStatus.arm().armY().toFixed(2);
+ this.circularIndex.innerHTML =
+ this.superstructureStatus.arm().armCircularIndex().toFixed(0);
+ this.roll.innerHTML = this.superstructureStatus.arm()
+ .rollJointEstimatorState()
+ .position()
+ .toFixed(2);
+ this.proximal.innerHTML = this.superstructureStatus.arm()
+ .proximalEstimatorState()
+ .position()
+ .toFixed(2);
+ this.distal.innerHTML = this.superstructureStatus.arm()
+ .distalEstimatorState()
+ .position()
+ .toFixed(2);
+ let zeroingErrors: string = 'Roll Joint Errors:' +
+ '<br/>';
+ for (let i = 0; i < this.superstructureStatus.arm()
+ .rollJointEstimatorState()
+ .errors.length;
+ i++) {
+ zeroingErrors += ZeroingError[this.superstructureStatus.arm()
+ .rollJointEstimatorState()
+ .errors(i)] +
+ '<br/>';
+ }
+ zeroingErrors += '<br/>' +
+ 'Proximal Joint Errors:' +
+ '<br/>';
+ for (let i = 0; i < this.superstructureStatus.arm()
+ .proximalEstimatorState()
+ .errors.length;
+ i++) {
+ zeroingErrors += ZeroingError[this.superstructureStatus.arm()
+ .proximalEstimatorState()
+ .errors(i)] +
+ '<br/>';
+ }
+ zeroingErrors += '<br/>' +
+ 'Distal Joint Errors:' +
+ '<br/>';
+ for (let i = 0; i <
+ this.superstructureStatus.arm().distalEstimatorState().errors.length;
+ i++) {
+ zeroingErrors += ZeroingError[this.superstructureStatus.arm()
+ .distalEstimatorState()
+ .errors(i)] +
+ '<br/>';
+ }
+ zeroingErrors += '<br/>' +
+ 'Wrist Errors:' +
+ '<br/>';
+ for (let i = 0;
+ i < this.superstructureStatus.wrist().estimatorState().errors.length;
+ i++) {
+ zeroingErrors += ZeroingError[this.superstructureStatus.wrist()
+ .estimatorState()
+ .errors(i)] +
+ '<br/>';
+ }
+ this.zeroingFaults.innerHTML = zeroingErrors;
}
if (this.drivetrainStatus && this.drivetrainStatus.trajectoryLogging()) {
this.drawRobot(
this.drivetrainStatus.trajectoryLogging().x(),
this.drivetrainStatus.trajectoryLogging().y(),
- this.drivetrainStatus.trajectoryLogging().theta(), "#000000FF",
+ this.drivetrainStatus.trajectoryLogging().theta(), '#000000FF',
false);
}
diff --git a/y2023/www/styles.css b/y2023/www/styles.css
index c486115..c2c44d2 100644
--- a/y2023/www/styles.css
+++ b/y2023/www/styles.css
@@ -7,37 +7,19 @@
display: inline-block
}
-#targets,
#readouts,
-#vision_readouts {
+#middle_readouts
+{
display: inline-block;
vertical-align: top;
float: right;
}
+
#legend {
display: inline-block;
}
-#outer_target {
- border: 1px solid black;
- width: 140px;
- background-color: white;
-}
-
-#inner_target {
- width: 60px;
- height: 60px;
- margin: 40px;
- border: 1px solid black;
- background-color: white;
-}
-
-#outer_target.targetted,
-#inner_target.targetted {
- background-color: green;
-}
-
table, th, td {
border: 1px solid black;
border-collapse: collapse;
@@ -54,7 +36,7 @@
width: 150px;
}
-.near {
+.connected, .near {
background-color: LightGreen;
border-radius: 10px;
}
@@ -79,3 +61,14 @@
padding: 5px;
text-align: right;
}
+
+#message_bridge_status > div {
+ display: table-row;
+ padding: 5px;
+}
+
+#message_bridge_status > div > div {
+ display: table-cell;
+ padding: 5px;
+ text-align: right;
+}