UNPKG

@foxglove/ros1

Version:

Standalone TypeScript implementation of the ROS 1 (Robot Operating System) protocol with a pluggable transport layer

105 lines 4.4 kB
"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