@foxglove/ros1
Version:
Standalone TypeScript implementation of the ROS 1 (Robot Operating System) protocol with a pluggable transport layer
105 lines • 4.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TcpPublisher = void 0;
const eventemitter3_1 = require("eventemitter3");
const TcpClient_1 = require("./TcpClient");
// Implements publishing support for the TCPROS transport. The actual TCP server
// is implemented in the passed in `server` (TcpServer). A `RosNode` instance
// uses a single `TcpPublisher` instance for all published topics, each incoming
// TCP connection sends a connection header that specifies which topic that
// connection is subscribing to.
class TcpPublisher extends eventemitter3_1.EventEmitter {
constructor({ server, nodeName, getConnectionId, getPublication, log }) {
super();
this._pendingClients = new Map();
this._shutdown = false;
// TcpServer handlers ////////////////////////////////////////////////////////
this._handleConnection = async (socket) => {
const noOp = () => {
// no-op
};
if (this._shutdown) {
socket.close().catch(noOp);
return;
}
// TcpClient must be instantiated before any async calls since it registers event handlers
// that may not be setup in time otherwise
const client = new TcpClient_1.TcpClient({
socket,
nodeName: this._nodeName,
getPublication: this._getPublication,
log: this._log,
});
const connectionId = this._getConnectionId();
this._pendingClients.set(connectionId, client);
client.on("subscribe", (topic, destinationCallerId) => {
this._pendingClients.delete(connectionId);
if (!this._shutdown) {
this.emit("connection", topic, connectionId, destinationCallerId, client);
}
});
client.on("error", (err) => {
if (!this._shutdown) {
this._log?.warn?.(`tcp client ${client.toString()} error: ${err}`);
this.emit("error", new Error(`TCP client ${client.toString()} error: ${err}`));
}
});
};
this._handleClose = () => {
if (!this._shutdown) {
this._log?.warn?.(`tcp server closed unexpectedly. shutting down tcp publisher`);
this.emit("error", new Error("TCP publisher closed unexpectedly"));
this._shutdown = true;
}
};
this._handleError = (err) => {
if (!this._shutdown) {
this._log?.warn?.(`tcp publisher error: ${err}`);
this.emit("error", err);
}
};
this._server = server;
this._nodeName = nodeName;
this._getConnectionId = getConnectionId;
this._getPublication = getPublication;
this._log = log;
// eslint-disable-next-line @typescript-eslint/no-misused-promises
server.on("connection", this._handleConnection);
server.on("close", this._handleClose);
server.on("error", this._handleError);
}
async address() {
return await this._server.address();
}
async publish(publication, message) {
const msgSize = publication.messageWriter.calculateByteSize(message);
const dataSize = 4 + msgSize;
const buffer = new ArrayBuffer(dataSize);
// Write the 4-byte size integer
new DataView(buffer, 0, 4).setUint32(0, msgSize, true);
// Write the serialized message data
const msgData = new Uint8Array(buffer, 4, dataSize - 4);
publication.messageWriter.writeMessage(message, msgData);
const data = new Uint8Array(buffer, 0, dataSize);
await publication.write(this.transportType(), data);
}
transportType() {
return "TCPROS";
}
listening() {
return !this._shutdown;
}
close() {
this._log?.debug?.(`stopping tcp publisher for ${this._nodeName}`);
this._shutdown = true;
this.removeAllListeners();
this._server.close();
for (const client of this._pendingClients.values()) {
client.removeAllListeners();
client.close();
}
this._pendingClients.clear();
}
}
exports.TcpPublisher = TcpPublisher;
//# sourceMappingURL=TcpPublisher.js.map