UNPKG

@mdf.js/mqtt-provider

Version:

MMS - MQTT Port for Javascript/Typescript

186 lines 7.33 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Port = void 0; /** * Copyright 2024 Mytra Control S.L. All rights reserved. * * Use of this source code is governed by an MIT-style license that can be found in the LICENSE file * or at https://opensource.org/licenses/MIT. */ const core_1 = require("@mdf.js/core"); const crash_1 = require("@mdf.js/crash"); const mqtt_1 = require("mqtt"); const config_1 = require("../config"); const DEFAULT_PING_CHECK_INTERVAL = 10000; /** MQTT port implementation */ class Port extends core_1.Layer.Provider.Port { /** * Implementation of functionalities of an MQTT port instance. * @param config - Port configuration options * @param logger - Port logger, to be used internally */ constructor(config, logger) { super(config, logger, typeof config.clientId === 'string' ? config.clientId : config_1.CONFIG_PROVIDER_BASE_NAME); /** Update the state of the connection*/ this.checkConnectionStatus = () => { const shouldBeHealthy = this.state; if (this.isHealthy === shouldBeHealthy) { return; } else if (shouldBeHealthy) { this.emit('healthy'); } else { // @ts-ignore - Test environment this.emit('unhealthy', this.updateLastError(new crash_1.Crash('Ping response not received'))); } this.isHealthy = shouldBeHealthy; }; /** * Manage the event of a new connection or reconnection to the MQTT broker. * @param connectACK - MQTT connection acknowledgement packet */ this.onConnect = (connectACK) => { // Stryker disable next-line all this.logger.debug(`Port connected`, this.uuid, this.name, connectACK); this.isConnected = true; }; /** Manage the event of a new reconnection to the MQTT broker. */ this.onReconnect = () => { // Stryker disable next-line all this.logger.debug(`Port reconnecting`, this.uuid, this.name); }; /** Manage the event of a new disconnection to the MQTT broker. */ this.onClose = () => { // Stryker disable next-line all this.logger.debug(`Port disconnected`, this.uuid, this.name); this.isConnected = false; }; /** * Manage the event of a new disconnection to the MQTT broker. * @param packet - MQTT disconnection packet */ this.onDisconnect = (packet) => { // Stryker disable next-line all this.logger.debug(`Port disconnection request from broker: ${JSON.stringify(packet, null, 2)}`, this.uuid, this.name, packet); }; /** Manage the event of a new offline state in the MQTT connection. */ this.onOffline = () => { // Stryker disable next-line all this.logger.debug(`Port offline`, this.uuid, this.name); }; /** * Update the last error in the port instance. * @param rawError - Error object * @returns Crash or Multi object */ this.updateLastError = (rawError) => { const cause = crash_1.Crash.from(rawError, this.uuid); this.logger.crash(cause, this.name); this.addCheck('lastError', { componentId: this.uuid, observedValue: cause.message, observedUnit: 'Last error', status: 'pass', output: cause.message, time: new Date().toISOString(), }); return cause; }; /** * Manage the event of a new error in the MQTT connection. * @param rawError - Error object */ this.onError = (rawError) => { const cause = this.updateLastError(rawError); if (this.isConnected) { this.emit('error', cause); } return cause; }; const cleanedOptions = { ...this.config, url: undefined, manualConnect: true }; this.instance = (0, mqtt_1.connect)(this.config.url, cleanedOptions); // Stryker disable next-line all this.logger.debug(`New instance of MQTT port created: ${this.uuid}`); this.isConnected = false; this.isHealthy = false; } /** Return the underlying port instance */ get client() { return this.instance; } /** Return the port state as a boolean value, true if the port is available, false in otherwise */ get state() { return this.isConnected && this.client.connected; } /** Initialize the port instance */ async start() { if (this.isConnected) { // Stryker disable next-line all this.logger.warn(`Port already started`, this.uuid, this.name); return; } return new Promise((resolve, reject) => { const onConnect = (connectACK) => { this.instance.removeListener('error', onError); this.onConnect(connectACK); this.eventsWrapping(this.instance); this.connectionChecker = setInterval(this.checkConnectionStatus, this.config.keepalive || DEFAULT_PING_CHECK_INTERVAL); resolve(); }; const onError = (error) => { this.instance.removeListener('connect', onConnect); this.instance.end(true, () => { reject(this.onError(error)); }); }; this.instance.once('connect', onConnect); this.instance.once('error', onError); this.instance.connect(); }); } /** Stop the port instance */ async stop() { if (!this.isConnected) { // Stryker disable next-line all this.logger.warn(`Port already stopped`, this.uuid, this.name); return; } await this.instance.endAsync(); this.onClose(); this.eventsUnwrapping(this.instance); if (this.connectionChecker) { clearInterval(this.connectionChecker); } } /** Close the port instance */ async close() { await this.stop(); } /** * Attach all the events and log for debugging * @param instance - client where the event managers will be attached */ eventsWrapping(instance) { instance.on('connect', this.onConnect); instance.on('reconnect', this.onReconnect); instance.on('close', this.onClose); instance.on('disconnect', this.onDisconnect); instance.on('offline', this.onOffline); instance.on('error', this.onError); } /** * Remove all the events listeners * @param instance - client where the event managers will be unattached */ eventsUnwrapping(instance) { instance.off('connect', this.onConnect); instance.off('reconnect', this.onReconnect); instance.off('close', this.onClose); instance.off('disconnect', this.onDisconnect); instance.off('offline', this.onOffline); instance.off('error', this.onError); } } exports.Port = Port; //# sourceMappingURL=Port.js.map