diff --git a/src/HistoryQuery/HistoryQuery.class.js b/src/HistoryQuery/HistoryQuery.class.js index 3b4999e718..016500c77f 100644 --- a/src/HistoryQuery/HistoryQuery.class.js +++ b/src/HistoryQuery/HistoryQuery.class.js @@ -42,7 +42,7 @@ class HistoryQuery { this.filePattern = config.filePattern this.cacheFolder = `${this.engine.cacheFolder}/${this.id}` this.dataCacheFolder = `${this.engine.cacheFolder}/${this.id}/${HistoryQuery.DATA_FOLDER}` - this.statusData = { status: this.status } + this.statusData = {} this.numberOfQueryParts = Math.round((this.endTime.getTime() - this.startTime.getTime()) / (1000 * this.config.settings.maxReadInterval)) if (!this.engine.eventEmitters[`/history/${this.id}/sse`]) { @@ -53,8 +53,7 @@ class HistoryQuery { } this.engine.eventEmitters[`/history/${this.id}/sse`].events = new EventEmitter() this.engine.eventEmitters[`/history/${this.id}/sse`].events.on('data', this.listener) - this.engine.eventEmitters[`/history/${this.id}/sse`].statusData = this.statusData - this.updateStatusDataStream() + this.updateStatusDataStream({ status: this.status }) } /** @@ -192,8 +191,7 @@ class HistoryQuery { for (const scanGroup of this.south.scanGroups) { const { scanMode } = scanGroup if (scanGroup.points && scanGroup.points.length) { - this.statusData.scanGroup = `${scanGroup.scanMode} - ${scanGroup.aggregate}` - this.updateStatusDataStream() + this.updateStatusDataStream({ scanGroup: `${scanGroup.scanMode} - ${scanGroup.aggregate}` }) // eslint-disable-next-line no-await-in-loop const exportResult = await this.exportScanMode(scanMode) if (exportResult === -1) { @@ -204,8 +202,7 @@ class HistoryQuery { this.logger.error(`scanMode ${scanMode} ignored: scanGroup.points undefined or empty`) } } - this.statusData.scanGroup = null - this.updateStatusDataStream() + this.updateStatusDataStream({ scanGroup: null }) } else { const exportResult = await this.exportScanMode(this.dataSource.scanMode) if (exportResult === -1) { @@ -283,9 +280,10 @@ class HistoryQuery { } else { intervalEndTime = this.endTime } - this.statusData.currentTime = startTime.toISOString() - this.statusData.progress = Math.round((this.south.queryParts[scanMode] / this.numberOfQueryParts) * 10000) / 100 - this.updateStatusDataStream() + this.updateStatusDataStream({ + currentTime: startTime.toISOString(), + progress: Math.round((this.south.queryParts[scanMode] / this.numberOfQueryParts) * 10000) / 100, + }) // Wait between the read interval iterations if (!firstIteration) { @@ -312,9 +310,10 @@ class HistoryQuery { this.south.queryParts[scanMode] = 0 // eslint-disable-next-line no-await-in-loop await this.south.setConfig(`queryPart-${scanMode}`, this.south.queryParts[scanMode]) - this.statusData.currentTime = startTime.toISOString() - this.statusData.progress = 100 - this.updateStatusDataStream() + this.updateStatusDataStream({ + currentTime: startTime.toISOString(), + progress: 100, + }) return 0 } @@ -327,8 +326,7 @@ class HistoryQuery { this.status = status this.config.status = status await this.engine.historyQueryRepository.update(this.config) - this.statusData.status = status - this.updateStatusDataStream() + this.updateStatusDataStream({ status }) } /** @@ -365,7 +363,9 @@ class HistoryQuery { } } - updateStatusDataStream() { + updateStatusDataStream(statusData = {}) { + this.statusData = { ...this.statusData, ...statusData } + this.engine.eventEmitters[`/history/${this.id}/sse`].statusData = this.statusData this.engine.eventEmitters[`/history/${this.id}/sse`]?.events?.emit('data', this.statusData) } } diff --git a/src/HistoryQuery/HistoryQueryEngine.class.js b/src/HistoryQuery/HistoryQueryEngine.class.js index 0a2d868e16..1b0f444022 100644 --- a/src/HistoryQuery/HistoryQueryEngine.class.js +++ b/src/HistoryQuery/HistoryQueryEngine.class.js @@ -59,7 +59,6 @@ class HistoryQueryEngine extends BaseEngine { * @returns {void} */ initializeStatusData() { - this.statusData = { ongoingHistoryQueryId: null } if (!this.eventEmitters['/history/engine/sse']) { this.eventEmitters['/history/engine/sse'] = {} } else { @@ -67,11 +66,12 @@ class HistoryQueryEngine extends BaseEngine { } this.eventEmitters['/history/engine/sse'].events = new EventEmitter() this.eventEmitters['/history/engine/sse'].events.on('data', this.listener) - this.eventEmitters['/history/engine/sse'].statusData = this.statusData - this.updateStatusDataStream() + this.updateStatusDataStream({ ongoingHistoryQueryId: null }) } - updateStatusDataStream() { + updateStatusDataStream(statusData = {}) { + this.statusData = { ...this.statusData, ...statusData } + this.eventEmitters['/history/engine/sse'].statusData = this.statusData this.eventEmitters['/history/engine/sse']?.events?.emit('data', this.statusData) } @@ -149,8 +149,7 @@ class HistoryQueryEngine extends BaseEngine { if (this.historyQuery) { await this.historyQuery.stop() - this.statusData.ongoingHistorQueryId = null - this.updateStatusDataStream() + this.updateStatusDataStream({ ongoingHistoryQueryId: null }) this.eventEmitters['/history/engine/sse']?.events?.removeAllListeners() this.eventEmitters['/history/engine/sse']?.stream?.destroy() this.historyQuery = null @@ -178,14 +177,12 @@ class HistoryQueryEngine extends BaseEngine { if (dataSourceToUse && applicationToUse) { this.historyQuery = new HistoryQuery(this, this.logger, historyQueryConfig, dataSourceToUse, applicationToUse) - this.statusData.ongoingHistoryQueryId = this.historyQuery.id - this.updateStatusDataStream() + this.updateStatusDataStream({ ongoingHistoryQueryId: this.historyQuery.id }) this.historyQuery.start() } } else { this.logger.info('No HistoryQuery to execute') - this.statusData.ongoingHistoryQueryId = null - this.updateStatusDataStream() + this.updateStatusDataStream({ ongoingHistoryQueryId: null }) } } diff --git a/src/client/About/About.jsx b/src/client/About/About.jsx index dd4dbd10f9..3ebf84852a 100644 --- a/src/client/About/About.jsx +++ b/src/client/About/About.jsx @@ -6,20 +6,20 @@ import { ConfigContext } from '../context/ConfigContext.jsx' import logo from '../OIBus.png' const About = () => { - const [staticStatus, setstaticStatus] = React.useState({}) - const [dynamicStatus, setdynamicStatus] = React.useState({}) + const [oibusInfo, setOibusInfo] = React.useState({}) + const [oibusStatus, setOibusStatus] = React.useState({}) const { setAlert } = React.useContext(AlertContext) const { activeConfig } = React.useContext(ConfigContext) /** - * Acquire the status + * Retrieve OIBus info * @returns {void} */ - const fetchStatus = () => { + const fetchOIBusInfo = () => { apis - .getStatus() + .getOIBusInfo() .then((response) => { - setstaticStatus(response) + setOibusInfo(response) }) .catch((error) => { console.error(error) @@ -35,7 +35,7 @@ const About = () => { * @returns {void} */ React.useEffect(() => { - fetchStatus() + fetchOIBusInfo() }, []) /** @@ -53,7 +53,7 @@ const About = () => { source.onmessage = (event) => { if (event && event.data) { const myData = JSON.parse(event.data) - setdynamicStatus(myData) + setOibusStatus(myData) } } return () => { @@ -64,56 +64,56 @@ const About = () => { return (
- {staticStatus && ( + {oibusInfo && ( logo Version: - {staticStatus.version} + {oibusInfo.version} Architecture: - {staticStatus.architecture} + {oibusInfo.architecture} currentDirectory: - {staticStatus.currentDirectory} + {oibusInfo.currentDirectory} nodeVersion: - {staticStatus.nodeVersion} + {oibusInfo.nodeVersion} Executable: - {staticStatus.executable} + {oibusInfo.executable} ConfigurationFile: - {staticStatus.configurationFile} + {oibusInfo.configurationFile} Hostname: - {staticStatus.hostname} + {oibusInfo.hostname} Operating System: - {staticStatus.osType} + {oibusInfo.osType} {' '} - {staticStatus.osRelease} + {oibusInfo.osRelease} Official site Copyright: - {staticStatus.copyright} + {oibusInfo.copyright} Licensed under the EUPL-1.2-or-later - {Object.entries(dynamicStatus) + {Object.entries(oibusStatus) .map(([key, value]) => (
diff --git a/src/client/About/About.spec.jsx b/src/client/About/About.spec.jsx index bf4fe770ae..f902249061 100644 --- a/src/client/About/About.spec.jsx +++ b/src/client/About/About.spec.jsx @@ -82,7 +82,7 @@ const status = { let resolve let reject const setAlert = jest.fn() -apis.getStatus = () => new Promise((_resolve, _reject) => { +apis.getOIBusInfo = () => new Promise((_resolve, _reject) => { resolve = _resolve reject = _reject }) diff --git a/src/client/TopHeader.jsx b/src/client/TopHeader.jsx index 767a1ac267..b0cf2032e2 100644 --- a/src/client/TopHeader.jsx +++ b/src/client/TopHeader.jsx @@ -11,7 +11,7 @@ const TopHeader = () => { const { newConfig, activeConfig } = React.useContext(ConfigContext) const [isOpen, setIsOpen] = React.useState(false) const { setAlert } = React.useContext(AlertContext) - const [status, setStatus] = React.useState({}) + const [oibusInfo, setOibusInfo] = React.useState({}) const location = useLocation() React.useEffect(() => { @@ -28,9 +28,9 @@ const TopHeader = () => { */ const fetchStatus = () => { apis - .getStatus() + .getOIBusInfo() .then((response) => { - setStatus(response) + setOibusInfo(response) }) .catch((error) => { console.error(error) @@ -67,7 +67,7 @@ const TopHeader = () => { About - {`version ${status.version}`} + {`version ${oibusInfo.version}`} diff --git a/src/client/services/apis.js b/src/client/services/apis.js index b77d438066..03d3ccf6a5 100644 --- a/src/client/services/apis.js +++ b/src/client/services/apis.js @@ -89,7 +89,7 @@ const deleteHistoryQuery = async (id, position) => deleteRequest(`/history-queri const getLastCompletedForHistoryQuery = async (id) => getRequest(`/history-queries/${id}/status`) const getLogs = (fromDate, toDate, verbosity) => getRequest(`/logs?fromDate=${fromDate || ''}&toDate=${toDate || ''}&verbosity=[${verbosity}]`) -const getStatus = () => getRequest('/status') +const getOIBusInfo = () => getRequest('/info') const getSouthStatus = (id) => getRequest(`/status/south/${id}`) const getNorthStatus = (id) => getRequest(`/status/north/${id}`) @@ -111,7 +111,7 @@ export default { deleteHistoryQuery, getLastCompletedForHistoryQuery, getLogs, - getStatus, + getOIBusInfo, getSouthStatus, getNorthStatus, reload, diff --git a/src/client/services/apis.spec.js b/src/client/services/apis.spec.js index 58e17f782f..0cd141f821 100644 --- a/src/client/services/apis.spec.js +++ b/src/client/services/apis.spec.js @@ -21,7 +21,7 @@ global.fetch = jest.fn().mockImplementation((uri) => { case '/config': jsonString = JSON.stringify({ config: testConfig }) break - case '/status': + case '/info': jsonString = JSON.stringify({ version: '1.0' }) break default: @@ -117,7 +117,7 @@ describe('apis', () => { }) it('check getStatus', async () => { - const result = await apis.getStatus() + const result = await apis.getOIBusInfo() expect(result).toEqual({ version: '1.0' }) }) }) diff --git a/src/engine/HealthSignal.class.js b/src/engine/HealthSignal.class.js index bf4490a8d8..4fb13c1290 100644 --- a/src/engine/HealthSignal.class.js +++ b/src/engine/HealthSignal.class.js @@ -92,12 +92,12 @@ class HealthSignal { /** * Retrieve status information from the engine * @param {boolean} verbose - return only the id when false, return full status when true - * @returns {object} - the status of oibus + * @returns {object} - the status of OIBus */ async prepareStatus(verbose) { - let status = { version: this.engine.getVersion() } + let status = await this.engine.getOIBusInfo() if (verbose) { - status = await this.engine.getStatus() + status = { ...status, ...this.engine.statusData } } return status } diff --git a/src/engine/HealthSignal.class.spec.js b/src/engine/HealthSignal.class.spec.js index 18e8abbc87..1329d44183 100644 --- a/src/engine/HealthSignal.class.spec.js +++ b/src/engine/HealthSignal.class.spec.js @@ -43,8 +43,7 @@ describe('HealthSignal', () => { proxies: [proxy], } engine.configService = { getConfig: () => ({ engineConfig }) } - engine.getStatus = jest.fn() - engine.getVersion = jest.fn() + engine.getOIBusInfo = jest.fn() engine.requestService = { httpSend: jest.fn() } it('should be properly initialized', () => { @@ -98,25 +97,24 @@ describe('HealthSignal', () => { it('should prepare simple status info when verbose is not enabled', async () => { const healthSignal = new HealthSignal(engine) - engine.getVersion.mockReturnValue('version') + engine.getOIBusInfo.mockReturnValue({ version: 'version' }) + engine.statusData = { randomStatusData: 'test' } const status = await healthSignal.prepareStatus(false) - expect(engine.getVersion).toHaveBeenCalledTimes(1) - expect(engine.getStatus).not.toHaveBeenCalled() + expect(engine.getOIBusInfo).toHaveBeenCalledTimes(1) expect(status).toEqual({ version: 'version' }) }) it('should prepare full status info when verbose is enabled', async () => { const healthSignal = new HealthSignal(engine) - engine.getVersion.mockReturnValue('ver') - engine.getStatus.mockReturnValue({ status: 'status', version: 'version' }) + engine.getOIBusInfo.mockReturnValue({ status: 'status', version: 'version' }) + engine.statusData = { randomStatusData: 'test' } const status = await healthSignal.prepareStatus(true) - expect(engine.getVersion).toHaveBeenCalledTimes(1) - expect(engine.getStatus).toHaveBeenCalledTimes(1) - expect(status).toEqual({ status: 'status', version: 'version' }) + expect(engine.getOIBusInfo).toHaveBeenCalledTimes(1) + expect(status).toEqual({ status: 'status', version: 'version', randomStatusData: 'test' }) }) it('should call RequestService httpSend()', async () => { diff --git a/src/engine/OIBusEngine.class.js b/src/engine/OIBusEngine.class.js index 40f000d5dd..66975f41ab 100644 --- a/src/engine/OIBusEngine.class.js +++ b/src/engine/OIBusEngine.class.js @@ -3,7 +3,6 @@ const path = require('path') const os = require('os') const EventEmitter = require('events') const humanizeDuration = require('humanize-duration') -const databaseService = require('../services/database.service') // Engine classes const BaseEngine = require('./BaseEngine.class') @@ -426,64 +425,23 @@ class OIBusEngine extends BaseEngine { * Get status information. * @returns {object} - The status information */ - async getStatus() { - const apisCacheStats = await this.cache.getCacheStatsForApis() - const protocolsCacheStats = await this.cache.getCacheStatsForProtocols() - const memoryUsage = this.getMemoryUsage() - - const freeMemory = Number(os.freemem() / 1024 / 1024).toFixed(2) - const totalMemory = Number(os.totalmem() / 1024 / 1024).toFixed(2) - const percentMemory = Number((freeMemory / totalMemory) * 100).toFixed(2) - - const { engineConfig } = this.configService.getConfig() - const logsCount = await databaseService.getLogsCount(engineConfig.logParameters.sqliteLog.fileName) - - const processUptime = 1000 * 1000 * process.uptime() - const processCpuUsage = process.cpuUsage() - const cpuUsagePercentage = Number((100 * (processCpuUsage.user + processCpuUsage.system)) / processUptime).toFixed(2) - - const filesErrorCount = await databaseService.getErroredFilesCount(this.cache.filesErrorDatabasePath) - const valuesErrorCount = await databaseService.getErroredValuesCount(this.cache.valuesErrorDatabasePath) - + async getOIBusInfo() { return { version: this.getVersion(), architecture: process.arch, - currentDirectory: process.cwd(), - nodeVersion: process.version, + 'Current directory': process.cwd(), + 'Node version': process.version, executable: process.execPath, - configurationFile: this.configService.getConfigurationFileLocation(), - historyQueryConfigurationFile: this.configService.getHistoryQueryConfigurationFileLocation(), - memory: `${freeMemory}/${totalMemory}/${percentMemory} MB/%`, - ...memoryUsage, - cpuUsage: `${cpuUsagePercentage}%`, + 'Configuration file': this.configService.getConfigurationFileLocation(), + 'History Query Database': this.configService.getHistoryQueryConfigurationFileLocation(), processId: process.pid, - uptime: humanizeDuration(1000 * process.uptime(), { round: true }), hostname: os.hostname(), osRelease: os.release(), osType: os.type(), - apisCacheStats, - protocolsCacheStats, - addValuesMessages: this.addValuesMessages, - addValuesCount: this.addValuesCount, - addFileCount: this.addFileCount, - logError: logsCount.error, - logWarning: logsCount.warn, - filesErrorCount, - valuesErrorCount, copyright: '(c) Copyright 2019-2022 Optimistik, all rights reserved.', } } - /** - * Get live status for a given South. - * @param {string} id - The datasource id - * @returns {object} - The live status - */ - getStatusForSouth(id) { - const south = this.activeProtocols[id] - return south ? south.getStatus() : {} - } - /** * Get cache folder * @return {string} - The cache folder @@ -493,35 +451,22 @@ class OIBusEngine extends BaseEngine { return engineConfig.caching.cacheFolder } - /** - * Get live status for a given North. - * @param {string} id - The application id - * @returns {object} - The live status - */ - getStatusForNorth(id) { - const north = this.activeApis[id] - return north ? north.getStatus() : {} - } - /** * Update engine status data to be displayed on the home screen * @returns {void} */ initializeStatusData() { - this.updateEngineStatusData() if (!this.eventEmitters['/engine/sse']) { this.eventEmitters['/engine/sse'] = {} } this.eventEmitters['/engine/sse'].events = new EventEmitter() this.eventEmitters['/engine/sse'].events.on('data', this.listener) - this.eventEmitters['/engine/sse'].statusData = this.statusData - this.updateStatusDataStream() + this.updateEngineStatusData() if (this.liveStatusInterval) { clearInterval(this.liveStatusInterval) } this.liveStatusInterval = setInterval(() => { this.updateEngineStatusData() - this.updateStatusDataStream() }, 5000) } @@ -538,39 +483,47 @@ class OIBusEngine extends BaseEngine { .toFixed(2) const memoryUsage = this.getMemoryUsage() - this.statusData['Up time'] = humanizeDuration(1000 * process.uptime(), { round: true }) - this.statusData['CPU usage'] = `${cpuUsagePercentage}%` - this.statusData['OS free memory'] = `${freeMemory} GB / ${totalMemory} GB (${percentMemory} %)` - this.statusData['RAM occupation (min / current / max)'] = memoryUsage.rss - this.statusData['Total heap size (min / current / max)'] = memoryUsage.heapTotal - this.statusData['Heap used (min / current / max)'] = memoryUsage.heapUsed - this.statusData['External C++ V8 memory (min / current / max)'] = memoryUsage.external - this.statusData['Array buffers memory (min / current / max)'] = memoryUsage.arrayBuffers - + const northConnectorsStatus = {} Object.values(this.activeApis).forEach((activeNorthConnector) => { if (activeNorthConnector.canHandleValues) { - this.statusData[`Number of values sent to North "${ + northConnectorsStatus[`Number of values sent to North "${ activeNorthConnector.application.name}"`] = activeNorthConnector.statusData['Number of values sent since OIBus has started'] } if (activeNorthConnector.canHandleFiles) { - this.statusData[`Number of files sent to North "${ + northConnectorsStatus[`Number of files sent to North "${ activeNorthConnector.application.name}"`] = activeNorthConnector.statusData['Number of files sent since OIBus has started'] } }) + const southConnectorsStatus = {} Object.values(this.activeProtocols).forEach((activeSouthConnector) => { if (activeSouthConnector.handlesPoints) { - this.statusData[`Number of values retrieved from South "${ + southConnectorsStatus[`Number of values retrieved from South "${ activeSouthConnector.dataSource.name}"`] = activeSouthConnector.statusData['Number of values since OIBus has started'] } if (activeSouthConnector.handlesFiles) { - this.statusData[`Number of files retrieved from South "${ + southConnectorsStatus[`Number of files retrieved from South "${ activeSouthConnector.dataSource.name}"`] = activeSouthConnector.statusData['Number of files since OIBus has started'] } }) + + this.updateStatusDataStream({ + 'Up time': humanizeDuration(1000 * process.uptime(), { round: true }), + 'CPU usage': `${cpuUsagePercentage}%`, + 'OS free memory': `${freeMemory} GB / ${totalMemory} GB (${percentMemory} %)`, + 'RAM occupation (min / current / max)': memoryUsage.rss, + 'Total heap size (min / current / max)': memoryUsage.heapTotal, + 'Heap used (min / current / max)': memoryUsage.heapUsed, + 'External C++ V8 memory (min / current / max)': memoryUsage.external, + 'Array buffers memory (min / current / max)': memoryUsage.arrayBuffers, + ...northConnectorsStatus, + ...southConnectorsStatus, + }) } - updateStatusDataStream() { + updateStatusDataStream(statusData = {}) { + this.statusData = { ...this.statusData, ...statusData } + this.eventEmitters['/engine/sse'].statusData = this.statusData this.eventEmitters['/engine/sse']?.events?.emit('data', this.statusData) } diff --git a/src/north/AmazonS3/AmazonS3.class.js b/src/north/AmazonS3/AmazonS3.class.js index fbbd415807..9f32cf784b 100644 --- a/src/north/AmazonS3/AmazonS3.class.js +++ b/src/north/AmazonS3/AmazonS3.class.js @@ -73,10 +73,11 @@ class AmazonS3 extends ApiHandler { this.logger.error(error) return ApiHandler.STATUS.COMMUNICATION_ERROR } - this.statusData['Last uploaded file'] = filePath - this.statusData['Number of files sent since OIBus has started'] += 1 - this.statusData['Last upload at'] = new Date().toISOString() - this.updateStatusDataStream() + this.updateStatusDataStream({ + 'Last uploaded file': filePath, + 'Number of files sent since OIBus has started': this.statusData['Number of files sent since OIBus has started'] + 1, + 'Last upload at': new Date().toISOString(), + }) return ApiHandler.STATUS.SUCCESS } diff --git a/src/north/ApiHandler.class.js b/src/north/ApiHandler.class.js index 6ecf158617..e0014c53a3 100644 --- a/src/north/ApiHandler.class.js +++ b/src/north/ApiHandler.class.js @@ -72,8 +72,7 @@ class ApiHandler { connect(additionalInfo) { const { name, api } = this.application this.connected = true - this.statusData['Connected at'] = new Date().toISOString() - this.updateStatusDataStream() + this.updateStatusDataStream({ 'Connected at': new Date().toISOString() }) if (additionalInfo) { this.logger.info(`North API ${name} started with protocol ${api} ${additionalInfo}`) } else { @@ -82,12 +81,6 @@ class ApiHandler { } initializeStatusData() { - if (this.canHandleValues) { - this.statusData['Number of values sent since OIBus has started'] = 0 - } - if (this.canHandleFiles) { - this.statusData['Number of files sent since OIBus has started'] = 0 - } if (!this.engine.eventEmitters[`/north/${this.application.id}/sse`]) { this.engine.eventEmitters[`/north/${this.application.id}/sse`] = {} } else { @@ -96,8 +89,10 @@ class ApiHandler { } this.engine.eventEmitters[`/north/${this.application.id}/sse`].events = new EventEmitter() this.engine.eventEmitters[`/north/${this.application.id}/sse`].events.on('data', this.listener) - this.engine.eventEmitters[`/north/${this.application.id}/sse`].statusData = this.statusData - this.updateStatusDataStream() + this.updateStatusDataStream({ + 'Number of values sent since OIBus has started': this.canHandleValues ? 0 : undefined, + 'Number of files sent since OIBus has started': this.canHandleFiles ? 0 : undefined, + }) } /** @@ -108,6 +103,7 @@ class ApiHandler { async disconnect() { this.connected = false const { name, id } = this.application + this.updateStatusDataStream({ 'Connected at': 'Not connected' }) this.logger.info(`North API ${name} (${id}) disconnected`) this.engine.eventEmitters[`/north/${id}/sse`]?.events?.removeAllListeners() this.engine.eventEmitters[`/north/${id}/sse`]?.stream?.destroy() @@ -124,7 +120,9 @@ class ApiHandler { } } - updateStatusDataStream() { + updateStatusDataStream(statusData = {}) { + this.statusData = { ...this.statusData, ...statusData } + this.engine.eventEmitters[`/north/${this.application.id}/sse`].statusData = this.statusData this.engine.eventEmitters[`/north/${this.application.id}/sse`]?.events?.emit('data', this.statusData) } diff --git a/src/north/Console/Console.class.js b/src/north/Console/Console.class.js index a7e7cf68f3..7e40b725d2 100644 --- a/src/north/Console/Console.class.js +++ b/src/north/Console/Console.class.js @@ -30,10 +30,11 @@ class Console extends ApiHandler { } else { process.stdout.write(`(${values.length})`) } - this.statusData['Last handled values at'] = new Date().toISOString() - this.statusData['Number of values sent since OIBus has started'] += values.length - this.statusData['Last added point id (value)'] = `${values[values.length - 1].pointId} (${JSON.stringify(values[values.length - 1].data)})` - this.updateStatusDataStream() + this.updateStatusDataStream({ + 'Last handled values at': new Date().toISOString(), + 'Number of values sent since OIBus has started': this.statusData['Number of values sent since OIBus has started'] + values.length, + 'Last added point id (value)': `${values[values.length - 1].pointId} (${JSON.stringify(values[values.length - 1].data)})`, + }) return values.length } @@ -50,10 +51,11 @@ class Console extends ApiHandler { fileSize, }] console.table(data) - this.statusData['Last uploaded file'] = filePath - this.statusData['Number of files sent since OIBus has started'] += 1 - this.statusData['Last upload at'] = new Date().toISOString() - this.updateStatusDataStream() + this.updateStatusDataStream({ + 'Last uploaded file': filePath, + 'Number of files sent since OIBus has started': this.statusData['Number of files sent since OIBus has started'] + 1, + 'Last upload at': new Date().toISOString(), + }) return ApiHandler.STATUS.SUCCESS } } diff --git a/src/north/CsvToHttp/CsvToHttp.class.js b/src/north/CsvToHttp/CsvToHttp.class.js index 2678b03416..5972b4d7eb 100644 --- a/src/north/CsvToHttp/CsvToHttp.class.js +++ b/src/north/CsvToHttp/CsvToHttp.class.js @@ -10,7 +10,7 @@ const REGEX_MATCH_VARIABLE_STRING = /^\${[^}]*}$/ // match if the string starts const REGEX_GET_VARIABLE = /[^${}]+/ // Get the value inside ${} /** - * Class CsvToHttp - convert a csv file into http request such as POST/PUT/PACTH + * Class CsvToHttp - convert a csv file into http request such as POST/PUT/PATCH */ class CsvToHttp extends ApiHandler { static category = 'API' @@ -51,7 +51,7 @@ class CsvToHttp extends ApiHandler { /** * Send the file. * @param {String} filePath - The path of the file - * @return {Promise} - The send status + * @return {Promise} - The status after sending a file */ async handleFile(filePath) { // Verify that the file receive is a csv one @@ -69,9 +69,9 @@ class CsvToHttp extends ApiHandler { return ApiHandler.STATUS.LOGIC_ERROR } - // The csv parsing is a success so we begin to map the content + // The csv parsing is a success, so we begin to map the content // Initialize the body to an empty array - const { httpBody, convertionErrorBuffer } = this.convertToHttpBody(csvDataParsed) + const { httpBody, conversionErrorBuffer } = this.convertToHttpBody(csvDataParsed) if (httpBody.length !== 0) { if (!await this.sendData(httpBody)) { @@ -79,16 +79,16 @@ class CsvToHttp extends ApiHandler { return ApiHandler.STATUS.LOGIC_ERROR } } - // Logs all the erros - if (convertionErrorBuffer.length > 0) { - this.logger.error(`${convertionErrorBuffer.length} convertions error`) + // Logs all the errors + if (conversionErrorBuffer.length > 0) { + this.logger.error(`${conversionErrorBuffer.length} conversions error`) - if (convertionErrorBuffer.length > ERROR_PRINT_SIZE) { + if (conversionErrorBuffer.length > ERROR_PRINT_SIZE) { for (let i = 0; i < ERROR_PRINT_SIZE; i += 1) { - this.logger.error(`${convertionErrorBuffer[i]}`) + this.logger.error(`${conversionErrorBuffer[i]}`) } } else { - convertionErrorBuffer.forEach((error) => { + conversionErrorBuffer.forEach((error) => { this.logger.error(`${error}`) }) } @@ -102,7 +102,7 @@ class CsvToHttp extends ApiHandler { */ parseCsvFile(filePath) { return new Promise((resolve, reject) => { - // Initialize array wich will be filled at each step + // Initialize array which will be filled at each step const csvDataParsed = [] const csvFile = fs.createReadStream(filePath) @@ -120,10 +120,11 @@ class CsvToHttp extends ApiHandler { }, complete: () => { this.logger.trace(`File ${filePath} parsed`) - this.statusData['Last Parsed file'] = filePath - this.statusData['Number of files sent since OIBus has started'] += 1 - this.statusData['Last Parsed file at'] = new Date().toISOString() - this.updateStatusDataStream() + this.updateStatusDataStream({ + 'Last Parsed file': filePath, + 'Number of files sent since OIBus has started': this.statusData['Number of files sent since OIBus has started'] + 1, + 'Last Parsed file at': new Date().toISOString(), + }) resolve(csvDataParsed) }, }) @@ -137,7 +138,7 @@ class CsvToHttp extends ApiHandler { convertToHttpBody(csvFileInJson) { const httpBody = [] // Reset the buffer for error - const convertionErrorBuffer = [] + const conversionErrorBuffer = [] // Log all headers in the csv file and log the value ine the mapping not present in headers this.logger.trace(`All available headers are: ${Object.keys(csvFileInJson[0])}`) @@ -148,25 +149,25 @@ class CsvToHttp extends ApiHandler { csvFileInJson.forEach((csvRowInJson, index) => { const { value, error } = CsvToHttp.convertCSVRowIntoHttpBody(csvRowInJson, onlyValidMappingValue) - // Test the result of the mapping/convertion before add it in the httpBody - // Add if we accept error convertion or if everything is fine + // Test the result of the mapping/conversion before add it in the httpBody + // Add if we accept error conversion or if everything is fine if ((!error && value) || (error && value && this.acceptUnconvertedRows)) { httpBody.push(value) } - // Add all the errors catched into the buffer + // Add all the errors caught into the buffer if (error) { error.forEach((err) => { - convertionErrorBuffer.push(`Line ${index + 1} in csv file: ${err}`) + conversionErrorBuffer.push(`Line ${index + 1} in csv file: ${err}`) }) } }) - return { httpBody, convertionErrorBuffer } + return { httpBody, conversionErrorBuffer } } /** * Get only the valid mapping fields - * @param {Mixed} allHeaders - available headears in the csvFile + * @param {object} allHeaders - available headers in the csvFile * @return {Array} - Return a bool */ getOnlyValidMappingValue(allHeaders) { @@ -189,7 +190,7 @@ class CsvToHttp extends ApiHandler { return new Promise((resolve, reject) => { try { if (httpBody.length > this.bodyMaxLength) { - // Divide the current body in array of maximun maxLength elements + // Divide the current body in array of maximum maxLength elements let i = 0 for (i; i < httpBody.length; i += this.bodyMaxLength) { this.sendRequest(httpBody.slice(i, i + this.bodyMaxLength - 1)) @@ -229,8 +230,8 @@ class CsvToHttp extends ApiHandler { /** * Test if the field is valid thanks to all headers - * @param {Mixed} allHeaders - available headears in the csvFile - * @param {Mixed} field - field to test if it matchs with the available headears + * @param {object} allHeaders - available headers in the csvFile + * @param {object} field - field to test if it matches with the available headers * @return {Boolean} - Return a bool */ static isHeaderValid(allHeaders, field) { @@ -306,10 +307,10 @@ class CsvToHttp extends ApiHandler { /** * It returns the concatenation of value with the previous object - * If the oject is empty it return the value sent - * @param {Mixed} currentJsonValue - currentJsonValue - * @param {Mixed} valueToAdd - valueToAdd - * @return {Mixed} - The converted value + * If the object is empty it return the value sent + * @param {object} currentJsonValue - currentJsonValue + * @param {object} valueToAdd - valueToAdd + * @return {object} - The converted value */ static insertValueInObject(currentJsonValue, valueToAdd) { if (currentJsonValue) { @@ -320,9 +321,9 @@ class CsvToHttp extends ApiHandler { /** * Convert the value in the selected type (string by default) - * @param {Mixed} valueToConvert - valueToConvert + * @param {*} valueToConvert - valueToConvert * @param {string} type - type - * @return {Mixed} - The converted value + * @return {object} - The converted value */ static convertToCorrectType(valueToConvert, type) { switch (type) { diff --git a/src/north/FileWriter/FileWriter.class.js b/src/north/FileWriter/FileWriter.class.js index 8161e077de..3d7a7c7f73 100644 --- a/src/north/FileWriter/FileWriter.class.js +++ b/src/north/FileWriter/FileWriter.class.js @@ -40,10 +40,11 @@ class FileWriter extends ApiHandler { try { await fs.writeFile(path.join(this.outputFolder, fileName), data) this.logger.debug(`FileWriter ${fileName} created in "${this.outputFolder}"`) - this.statusData['Last handled values at'] = new Date().toISOString() - this.statusData['Number of values sent since OIBus has started'] += values.length - this.statusData['Last added point id (value)'] = `${values[values.length - 1].pointId} (${values[values.length - 1].data.value})` - this.updateStatusDataStream() + this.updateStatusDataStream({ + 'Last handled values at': new Date().toISOString(), + 'Number of values sent since OIBus has started': this.statusData['Number of values sent since OIBus has started'] + values.length, + 'Last added point id (value)': `${values[values.length - 1].pointId} (${JSON.stringify(values[values.length - 1].data)})`, + }) return values.length } catch (error) { this.logger.error(`Error handling values: ${error}`) @@ -68,7 +69,11 @@ class FileWriter extends ApiHandler { this.statusData['Last uploaded file'] = filePath this.statusData['Number of files sent since OIBus has started'] += 1 this.statusData['Last upload at'] = new Date().toISOString() - this.updateStatusDataStream() + this.updateStatusDataStream({ + 'Last uploaded file': filePath, + 'Number of files sent since OIBus has started': this.statusData['Number of files sent since OIBus has started'] + 1, + 'Last upload at': new Date().toISOString(), + }) return ApiHandler.STATUS.SUCCESS } catch (error) { this.logger.error(`Error handling file, ${error}`) diff --git a/src/north/InfluxDB/InfluxDB.class.js b/src/north/InfluxDB/InfluxDB.class.js index 08e98340cb..033ce506d1 100644 --- a/src/north/InfluxDB/InfluxDB.class.js +++ b/src/north/InfluxDB/InfluxDB.class.js @@ -55,10 +55,11 @@ class InfluxDB extends ApiHandler { this.logger.trace(`InfluxDB handleValues() call with ${values.length} values`) try { await this.makeRequest(values) - this.statusData['Last handled values at'] = new Date().toISOString() - this.statusData['Number of values sent since OIBus has started'] += values.length - this.statusData['Last added point id (value)'] = `${values[values.length - 1].pointId} (${JSON.stringify(values[values.length - 1].data)})` - this.updateStatusDataStream() + this.updateStatusDataStream({ + 'Last handled values at': new Date().toISOString(), + 'Number of values sent since OIBus has started': this.statusData['Number of values sent since OIBus has started'] + values.length, + 'Last added point id (value)': `${values[values.length - 1].pointId} (${JSON.stringify(values[values.length - 1].data)})`, + }) } catch (error) { this.logger.error(error) throw error diff --git a/src/north/MQTT/MQTT.class.js b/src/north/MQTT/MQTT.class.js index 2ed6c3614e..eb307264cf 100644 --- a/src/north/MQTT/MQTT.class.js +++ b/src/north/MQTT/MQTT.class.js @@ -63,10 +63,11 @@ class MQTT extends ApiHandler { if (successCount === 0) { throw ApiHandler.STATUS.COMMUNICATION_ERROR } - this.statusData['Last handled values at'] = new Date().toISOString() - this.statusData['Number of values sent since OIBus has started'] += values.length - this.statusData['Last added point id (value)'] = `${values[values.length - 1].pointId} (${JSON.stringify(values[values.length - 1].data)})` - this.updateStatusDataStream() + this.updateStatusDataStream({ + 'Last handled values at': new Date().toISOString(), + 'Number of values sent since OIBus has started': this.statusData['Number of values sent since OIBus has started'] + values.length, + 'Last added point id (value)': `${values[values.length - 1].pointId} (${JSON.stringify(values[values.length - 1].data)})`, + }) return successCount } @@ -117,8 +118,6 @@ class MQTT extends ApiHandler { async disconnect() { this.logger.info(`Disconnecting ${this.application.name} from ${this.url}`) this.client.end(true) - this.statusData['Connected at'] = 'Not connected' - this.updateStatusDataStream() await super.disconnect() } diff --git a/src/north/MongoDB/MongoDB.class.js b/src/north/MongoDB/MongoDB.class.js index fd115fbbf5..f1842205d2 100644 --- a/src/north/MongoDB/MongoDB.class.js +++ b/src/north/MongoDB/MongoDB.class.js @@ -56,10 +56,11 @@ class MongoDB extends ApiHandler { this.logger.trace(`MongoDB handleValues() call with ${values.length} values`) try { await this.makeRequest(values) - this.statusData['Last handled values at'] = new Date().toISOString() - this.statusData['Number of values sent since OIBus has started'] += values.length - this.statusData['Last added point id (value)'] = `${values[values.length - 1].pointId} (${JSON.stringify(values[values.length - 1].data)})` - this.updateStatusDataStream() + this.updateStatusDataStream({ + 'Last handled values at': new Date().toISOString(), + 'Number of values sent since OIBus has started': this.statusData['Number of values sent since OIBus has started'] + values.length, + 'Last added point id (value)': `${values[values.length - 1].pointId} (${JSON.stringify(values[values.length - 1].data)})`, + }) } catch (error) { this.logger.error(error) throw ApiHandler.STATUS.COMMUNICATION_ERROR @@ -115,8 +116,6 @@ class MongoDB extends ApiHandler { await this.client.close() } await super.disconnect() - this.statusData['Connected at'] = 'Not connected' - this.updateStatusDataStream() } /** diff --git a/src/north/OIAnalytics/OIAnalytics.class.js b/src/north/OIAnalytics/OIAnalytics.class.js index 809fac20bf..4c68989c0c 100644 --- a/src/north/OIAnalytics/OIAnalytics.class.js +++ b/src/north/OIAnalytics/OIAnalytics.class.js @@ -53,10 +53,11 @@ class OIAnalytics extends ApiHandler { })) await this.postJson(cleanedValues) this.logger.debug(`OIAnalytics ${this.application.name} has posted ${cleanedValues.length} values`) - this.statusData['Last handled values at'] = new Date().toISOString() - this.statusData['Number of values sent since OIBus has started'] += values.length - this.statusData['Last added point id (value)'] = `${values[values.length - 1].pointId} (${values[values.length - 1].data.value})` - this.updateStatusDataStream() + this.updateStatusDataStream({ + 'Last handled values at': new Date().toISOString(), + 'Number of values sent since OIBus has started': this.statusData['Number of values sent since OIBus has started'] + values.length, + 'Last added point id (value)': `${values[values.length - 1].pointId} (${JSON.stringify(values[values.length - 1].data)})`, + }) return values.length } @@ -70,10 +71,11 @@ class OIAnalytics extends ApiHandler { this.logger.debug(`OIAnalytics ${this.application.name} handleFile(${filePath}) (${stats.size} bytes)`) const result = await this.postFile(filePath) - this.statusData['Last uploaded file'] = filePath - this.statusData['Number of files sent since OIBus has started'] += 1 - this.statusData['Last upload at'] = new Date().toISOString() - this.updateStatusDataStream() + this.updateStatusDataStream({ + 'Last uploaded file': filePath, + 'Number of files sent since OIBus has started': this.statusData['Number of files sent since OIBus has started'] + 1, + 'Last upload at': new Date().toISOString(), + }) return result } } diff --git a/src/north/OIConnect/OIConnect.class.js b/src/north/OIConnect/OIConnect.class.js index 764289ada8..f45348704e 100644 --- a/src/north/OIConnect/OIConnect.class.js +++ b/src/north/OIConnect/OIConnect.class.js @@ -31,10 +31,11 @@ class OIConnect extends ApiHandler { * @return {Promise} - The handle status */ async handleValues(values) { - this.statusData['Last handled values at'] = new Date().toISOString() - this.statusData['Number of values sent since OIBus has started'] += values.length - this.statusData['Last added point id (value)'] = `${values[values.length - 1].pointId} (${JSON.stringify(values[values.length - 1].data)})` - this.updateStatusDataStream() + this.updateStatusDataStream({ + 'Last handled values at': new Date().toISOString(), + 'Number of values sent since OIBus has started': this.statusData['Number of values sent since OIBus has started'] + values.length, + 'Last added point id (value)': `${values[values.length - 1].pointId} (${JSON.stringify(values[values.length - 1].data)})`, + }) await this.postJson(values) this.logger.debug(`OIConnect ${this.application.name} has posted ${values.length} values`) return values.length @@ -48,10 +49,11 @@ class OIConnect extends ApiHandler { async handleFile(filePath) { const stats = await fs.stat(filePath) this.logger.debug(`OIConnect ${this.application.name} handleFile(${filePath}) (${stats.size} bytes)`) - this.statusData['Last uploaded file'] = filePath - this.statusData['Number of files sent since OIBus has started'] += 1 - this.statusData['Last upload at'] = new Date().toISOString() - this.updateStatusDataStream() + this.updateStatusDataStream({ + 'Last uploaded file': filePath, + 'Number of files sent since OIBus has started': this.statusData['Number of files sent since OIBus has started'] + 1, + 'Last upload at': new Date().toISOString(), + }) return this.postFile(filePath) } } diff --git a/src/north/TimescaleDB/TimescaleDB.class.js b/src/north/TimescaleDB/TimescaleDB.class.js index bdacb5a11a..8acaccf1b7 100644 --- a/src/north/TimescaleDB/TimescaleDB.class.js +++ b/src/north/TimescaleDB/TimescaleDB.class.js @@ -49,10 +49,11 @@ class TimescaleDB extends ApiHandler { this.logger.trace(`TimescaleDB handleValues() call with ${values.length} values`) try { await this.makeRequest(values) - this.statusData['Last handled values at'] = new Date().toISOString() - this.statusData['Number of values sent since OIBus has started'] += values.length - this.statusData['Last added point id (value)'] = `${values[values.length - 1].pointId} (${JSON.stringify(values[values.length - 1].data)})` - this.updateStatusDataStream() + this.updateStatusDataStream({ + 'Last handled values at': new Date().toISOString(), + 'Number of values sent since OIBus has started': this.statusData['Number of values sent since OIBus has started'] + values.length, + 'Last added point id (value)': `${values[values.length - 1].pointId} (${JSON.stringify(values[values.length - 1].data)})`, + }) } catch (error) { this.logger.error(error) throw ApiHandler.STATUS.COMMUNICATION_ERROR diff --git a/src/north/WATSYConnect/WATSYConnect.class.js b/src/north/WATSYConnect/WATSYConnect.class.js index 739b69c072..14cafc1bef 100644 --- a/src/north/WATSYConnect/WATSYConnect.class.js +++ b/src/north/WATSYConnect/WATSYConnect.class.js @@ -104,26 +104,25 @@ class WATSYConnect extends ApiHandler { */ async disconnect() { this.client.end(true) - this.statusData['Connected at'] = 'Not connected' - this.updateStatusDataStream() await super.disconnect() } /** * Handle messages by sending them to WATSYConnect North. - * @param {object[]} messages - The messages + * @param {object[]} values - The messages * @return {Promise} - The handle status */ - async handleValues(messages) { - this.logger.trace(`Link handleValues() call with ${messages.length} messages`) - const successCount = await this.publishOIBusMessages(messages) + async handleValues(values) { + this.logger.trace(`Link handleValues() call with ${values.length} messages`) + const successCount = await this.publishOIBusMessages(values) if (successCount === 0) { throw ApiHandler.STATUS.COMMUNICATION_ERROR } - this.statusData['Last handled values at'] = new Date().toISOString() - this.statusData['Number of values sent since OIBus has started'] += messages.length - this.statusData['Last added point id (value)'] = `${messages[messages.length - 1].pointId} (${messages[messages.length - 1].data.value})` - this.updateStatusDataStream() + this.updateStatusDataStream({ + 'Last handled values at': new Date().toISOString(), + 'Number of values sent since OIBus has started': this.statusData['Number of values sent since OIBus has started'] + values.length, + 'Last added point id (value)': `${values[values.length - 1].pointId} (${JSON.stringify(values[values.length - 1].data)})`, + }) return successCount } diff --git a/src/server/controllers/engineController.js b/src/server/controllers/engineController.js index 2b4f6664f0..9c9670a2c0 100644 --- a/src/server/controllers/engineController.js +++ b/src/server/controllers/engineController.js @@ -3,27 +3,8 @@ * @param {Object} ctx - The KOA context * @return {void} */ -const getStatus = async (ctx) => { - const status = await ctx.app.engine.getStatus() - ctx.ok(status) -} - -/** - * Get live status for a given South - * @param {Object} ctx - The KOA context - * @return {void} - */ -const getStatusForSouth = async (ctx) => { - const status = await ctx.app.engine.getStatusForSouth(ctx.params.id) - ctx.ok(status) -} -/** - * Get live status for a given North - * @param {Object} ctx - The KOA context - * @return {void} - */ -const getStatusForNorth = async (ctx) => { - const status = await ctx.app.engine.getStatusForNorth(ctx.params.id) +const getOIBusInfo = async (ctx) => { + const status = await ctx.app.engine.getOIBusInfo() ctx.ok(status) } @@ -105,9 +86,7 @@ const aliveSignal = async (ctx) => { } module.exports = { - getStatus, - getStatusForSouth, - getStatusForNorth, + getOIBusInfo, getNorthList, getSouthList, addValues, diff --git a/src/server/routes/index.js b/src/server/routes/index.js index 85688e3a58..253b996a00 100644 --- a/src/server/routes/index.js +++ b/src/server/routes/index.js @@ -24,8 +24,7 @@ router.get('/config/schemas/south', engineController.getSouthList) router.post('/engine/addValues', engineController.addValues) router.post('/engine/addFile', upload.single('file'), engineController.addFile) router.post('/engine/aliveSignal', engineController.aliveSignal) -router.get('/status', engineController.getStatus) -router.get('/status/south/:id', engineController.getStatusForSouth) +router.get('/info', engineController.getOIBusInfo) router.get('/reload', oibusController.reload) router.get('/shutdown', oibusController.shutdown) router.get('/logs', logController.getLogs) diff --git a/src/south/ADS/ADS.class.js b/src/south/ADS/ADS.class.js index 5a808aa5dd..2c5d25d904 100644 --- a/src/south/ADS/ADS.class.js +++ b/src/south/ADS/ADS.class.js @@ -237,17 +237,10 @@ class ADS extends ProtocolHandler { try { const result = await this.client.connect() this.connected = true - this.statusData['Connected at'] = new Date().toISOString() - this.updateStatusDataStream() + this.updateStatusDataStream({ 'Connected at': new Date().toISOString() }) this.logger.info(`Connected to the ${result.targetAmsNetId} with local AmsNetId ${result.localAmsNetId} and local port ${result.localAdsPort}`) } catch (error) { - this.connected = false - this.statusData['Connected at'] = 'Not connected' - this.updateStatusDataStream() this.logger.error(`ADS connect error: ${JSON.stringify(error)}`) - if (this.reconnectTimeout) { - clearTimeout(this.reconnectTimeout) - } this.reconnectTimeout = setTimeout(this.connectToAdsServer.bind(this), this.retryInterval) } } @@ -282,8 +275,6 @@ class ADS extends ProtocolHandler { } this.logger.info(`ADS client disconnected from ${this.netId}:${this.port}`) this.connected = false - this.statusData['Connected at'] = 'Not connected' - this.updateStatusDataStream() this.client = null } await super.disconnect() diff --git a/src/south/MQTT/MQTT.class.js b/src/south/MQTT/MQTT.class.js index 895c6968f4..52c3c10df8 100644 --- a/src/south/MQTT/MQTT.class.js +++ b/src/south/MQTT/MQTT.class.js @@ -85,9 +85,10 @@ class MQTT extends ProtocolHandler { * @return {void} */ async connect() { - this.statusData['Connected at'] = 'Not connected' - this.statusData['Last scan at'] = 'Subscription' - this.updateStatusDataStream() + this.updateStatusDataStream({ + 'Connected at': 'Not connected', + 'Last scan at': 'Subscription', + }) await super.connect() this.logger.info(`Connecting to ${this.url}...`) @@ -130,8 +131,6 @@ class MQTT extends ProtocolHandler { */ handleConnectError(error) { this.logger.error(error) - this.statusData['Connected at'] = 'Not connected' - this.updateStatusDataStream() } /** @@ -142,8 +141,7 @@ class MQTT extends ProtocolHandler { this.logger.info(`Connected to ${this.url}`) this.listen({ pointList: this.dataSource.points }) - this.statusData['Connected at'] = new Date().toISOString() - this.updateStatusDataStream() + this.updateStatusDataStream({ 'Connected at': new Date().toISOString() }) } /** diff --git a/src/south/Modbus/Modbus.class.js b/src/south/Modbus/Modbus.class.js index 45545184fe..e87d975107 100644 --- a/src/south/Modbus/Modbus.class.js +++ b/src/south/Modbus/Modbus.class.js @@ -141,8 +141,6 @@ class Modbus extends ProtocolHandler { if (this.connected) { this.socket.end() this.connected = false - this.statusData['Connected at'] = 'Not connected' - this.updateStatusDataStream() } await super.disconnect() } @@ -156,8 +154,7 @@ class Modbus extends ProtocolHandler { { host, port }, () => { this.connected = true - this.statusData['Connected at'] = new Date().toISOString() - this.updateStatusDataStream() + this.updateStatusDataStream({ 'Connected at': new Date().toISOString() }) }, ) this.socket.on('error', (error) => { diff --git a/src/south/OPCHDA/OPCHDA.class.js b/src/south/OPCHDA/OPCHDA.class.js index fda6267889..e6c861e074 100644 --- a/src/south/OPCHDA/OPCHDA.class.js +++ b/src/south/OPCHDA/OPCHDA.class.js @@ -66,6 +66,8 @@ class OPCHDA extends ProtocolHandler { await super.connect() await this.runTcpServer() await this.connection$.promise + this.connected = true + this.updateStatusDataStream({ 'Connected at': new Date().toISOString() }) } else { this.logger.error(`OIBus OPCHDA Agent only supported on Windows: ${process.platform}`) } @@ -80,7 +82,6 @@ class OPCHDA extends ProtocolHandler { try { const { agentFilename, tcpPort, logLevel } = this.dataSource.OPCHDA this.tcpServer = new TcpServer(tcpPort, this.handleMessage.bind(this), this.logger) - this.tcpServer.start(() => { this.launchAgent(agentFilename, tcpPort, logLevel) this.connection$ = new DeferredPromise() @@ -137,11 +138,7 @@ class OPCHDA extends ProtocolHandler { } this.tcpServer = null - this.agentConnected = false - - this.statusData['Connected at'] = 'Not connected' - this.updateStatusDataStream() await super.disconnect() } @@ -306,8 +303,7 @@ class OPCHDA extends ProtocolHandler { const messageString = JSON.stringify(message) this.logger.trace(`Sent at ${new Date().toISOString()}: ${messageString}`) this.tcpServer.sendMessage(messageString) - this.statusData['Last message sent at'] = new Date().toISOString() - this.updateStatusDataStream() + this.updateStatusDataStream({ 'Last message sent at': new Date().toISOString() }) } else { this.logger.debug(`sendMessage ignored, TCP server: ${this.tcpServer}, agent connected: ${this.agentConnected}`) } @@ -348,12 +344,9 @@ class OPCHDA extends ProtocolHandler { } break case 'Initialize': // The HDA Agent is connected and ready to read values - this.connected = true - this.statusData['Connected at'] = new Date().toISOString() - this.updateStatusDataStream() + this.logger.info(`HDA Agent initialized: ${this.connected}`) // resolve the connection promise this.connection$.resolve() - this.logger.info(`HDA Agent initialized: ${this.connected}`) break case 'Read': // Receive the values for the requested scan group (Content.Group) after a read request from historyQuery { diff --git a/src/south/OPCUA/OPCUA_DA/OPCUA_DA.class.js b/src/south/OPCUA/OPCUA_DA/OPCUA_DA.class.js index c95915caae..e9c2cd4e76 100644 --- a/src/south/OPCUA/OPCUA_DA/OPCUA_DA.class.js +++ b/src/south/OPCUA/OPCUA_DA/OPCUA_DA.class.js @@ -133,9 +133,6 @@ class OPCUA_DA extends ProtocolHandler { if (this.connected) { await this.session.close() await this.client.disconnect() - this.connected = false - this.statusData['Connected at'] = 'Not connected' - this.updateStatusDataStream() } await super.disconnect() } @@ -223,8 +220,7 @@ class OPCUA_DA extends ProtocolHandler { this.session = await this.client.createSession(userIdentity) this.connected = true this.logger.info('OPCUA_DA Connected') - this.statusData['Connected at'] = new Date().toISOString() - this.updateStatusDataStream() + this.updateStatusDataStream({ 'Connected at': new Date().toISOString() }) } catch (error) { this.logger.error(error) if (this.reconnectTimeout) { diff --git a/src/south/OPCUA/OPCUA_HA/OPCUA_HA.class.js b/src/south/OPCUA/OPCUA_HA/OPCUA_HA.class.js index d5b1657e12..45ee4f06e0 100644 --- a/src/south/OPCUA/OPCUA_HA/OPCUA_HA.class.js +++ b/src/south/OPCUA/OPCUA_HA/OPCUA_HA.class.js @@ -384,9 +384,6 @@ class OPCUA_HA extends ProtocolHandler { if (this.connected) { await this.session.close() await this.client.disconnect() - this.connected = false - this.statusData['Connected at'] = 'Not connected' - this.updateStatusDataStream() } await super.disconnect() } @@ -433,8 +430,7 @@ class OPCUA_HA extends ProtocolHandler { this.session = await this.client.createSession(userIdentity) this.connected = true this.logger.info('OPCUA_HA Connected') - this.statusData['Connected at'] = new Date().toISOString() - this.updateStatusDataStream() + this.updateStatusDataStream({ 'Connected at': new Date().toISOString() }) } catch (error) { this.logger.error(error) if (this.reconnectTimeout) { diff --git a/src/south/ProtocolHandler.class.js b/src/south/ProtocolHandler.class.js index 0dc5021c37..539105e64a 100644 --- a/src/south/ProtocolHandler.class.js +++ b/src/south/ProtocolHandler.class.js @@ -55,10 +55,11 @@ class ProtocolHandler { this.addFileCount = 0 this.addPointsCount = 0 + this.statusData = {} + this.currentlyOnScan = {} this.buffer = [] this.bufferTimeout = null - this.statusData = {} this.keyFile = null this.certFile = null @@ -184,7 +185,6 @@ class ProtocolHandler { } this.engine.eventEmitters[`/south/${this.dataSource.id}/sse`].events = new EventEmitter() this.engine.eventEmitters[`/south/${this.dataSource.id}/sse`].events.on('data', this.listener) - this.engine.eventEmitters[`/south/${this.dataSource.id}/sse`].statusData = this.statusData this.updateStatusDataStream() } @@ -199,7 +199,9 @@ class ProtocolHandler { } } - updateStatusDataStream() { + updateStatusDataStream(statusData = {}) { + this.statusData = { ...this.statusData, ...statusData } + this.engine.eventEmitters[`/south/${this.dataSource.id}/sse`].statusData = this.statusData this.engine.eventEmitters[`/south/${this.dataSource.id}/sse`]?.events?.emit('data', this.statusData) } @@ -274,8 +276,7 @@ class ProtocolHandler { this.currentlyOnScan[scanMode] ??= 0 // initialize if undefined if (this.currentlyOnScan[scanMode] === 0) { this.currentlyOnScan[scanMode] = 1 - this.statusData['Last scan at'] = new Date().toLocaleString() - this.updateStatusDataStream() + this.updateStatusDataStream({ 'Last scan at': new Date().toLocaleString() }) try { if (this.supportedModes?.supportLastPoint) { await this.lastPointQuery(scanMode) @@ -303,6 +304,7 @@ class ProtocolHandler { id, } = this.dataSource this.connected = false + this.updateStatusDataStream({ 'Connected at': 'Not connected' }) this.logger.info(`Data source ${name} (${id}) disconnected`) this.engine.eventEmitters[`/south/${id}/sse`]?.events?.removeAllListeners() this.engine.eventEmitters[`/south/${id}/sse`]?.stream?.destroy() @@ -321,11 +323,12 @@ class ProtocolHandler { await this.engine.addValues(this.dataSource.id, bufferSave) if (bufferSave.length > 0) { - this.statusData['Number of values since OIBus has started'] += bufferSave.length - this.statusData['Last added points at'] = new Date().toLocaleString() - // eslint-disable-next-line max-len - this.statusData['Last added point id (value)'] = `${bufferSave[bufferSave.length - 1].pointId} (${JSON.stringify(bufferSave[bufferSave.length - 1].data)})` - this.updateStatusDataStream() + this.addPointsCount += bufferSave.length + this.updateStatusDataStream({ + 'Number of values since OIBus has started': this.addPointsCount, + 'Last added points at': new Date().toLocaleString(), + 'Last added point id (value)': `${bufferSave[bufferSave.length - 1].pointId} (${JSON.stringify(bufferSave[bufferSave.length - 1].data)})`, + }) } if (this.bufferTimeout) { @@ -340,8 +343,6 @@ class ProtocolHandler { * @return {void} */ async addValues(values) { - // used for status - this.addPointsCount += values.length // add new values to the protocol buffer this.buffer.push(...values) // if the protocol buffer is large enough, send it @@ -363,10 +364,11 @@ class ProtocolHandler { */ addFile(filePath, preserveFiles) { this.addFileCount += 1 - this.statusData['Number of files since OIBus has started'] += 1 - this.statusData['Last added file at'] = new Date().toLocaleString() - this.statusData['Last added file'] = filePath - this.updateStatusDataStream() + this.updateStatusDataStream({ + 'Number of files since OIBus has started': this.addFileCount, + 'Last added file at': new Date().toLocaleString(), + 'Last added file': filePath, + }) this.engine.addFile(this.dataSource.id, filePath, preserveFiles) } @@ -455,41 +457,6 @@ class ProtocolHandler { }) } - /** - * Get live status. - * @returns {object} - The live status - */ - getStatus() { - const status = { - Name: this.dataSource.name, - Id: this.dataSource.id, - 'Last scan time': this.statusData['Last scan at'] ? this.statusData['Last scan at'] : 'Never', - } - if (this.handlesFiles) { - status['Last file added time'] = this.statusData['Last added file at'] ? this.statusData['Last added file at'] : 'Never' - status['Number of files added'] = this.addFileCount - } - if (this.handlesPoints) { - status['Last values added time'] = this.statusData['Last added points at'] ? this.statusData['Last added points at'] : 'Never' - status['Number of values added'] = this.addPointsCount - } - if (this.canHandleHistory) { - if (this.lastCompletedAt) { - if (typeof this.lastCompletedAt === 'object') { - Object.entries(this.lastCompletedAt) - .forEach(([key, value]) => { - status[`Last completed at - ${key}`] = new Date(value).toLocaleString() - }) - } else { - status['Last completed at'] = new Date(this.lastCompletedAt).toLocaleString() - } - } else { - status['Last completed at'] = 'Never' - } - } - return status - } - /** * Get timestamp. * @param {string} elementTimestamp - The element timestamp diff --git a/src/south/RestApi/RestApi.class.js b/src/south/RestApi/RestApi.class.js index 44d79c1f73..3a16d09a14 100644 --- a/src/south/RestApi/RestApi.class.js +++ b/src/south/RestApi/RestApi.class.js @@ -120,8 +120,6 @@ class RestApi extends ProtocolHandler { async connect() { await super.connect() - this.statusData['Connected at'] = new Date().toISOString() - this.updateStatusDataStream() this.connected = true } diff --git a/src/south/SQL/SQL.class.js b/src/south/SQL/SQL.class.js index 23d8e78df1..ce2424bf3f 100644 --- a/src/south/SQL/SQL.class.js +++ b/src/south/SQL/SQL.class.js @@ -108,8 +108,6 @@ class SQL extends ProtocolHandler { async connect() { await super.connect() - this.statusData['Connected at'] = new Date().toISOString() - this.updateStatusDataStream() this.connected = true } @@ -511,8 +509,7 @@ class SQL extends ProtocolHandler { const startTimeLog = query.indexOf('@StartTime') !== -1 ? `StartTime = ${startTime.toISOString()}` : '' const endTimeLog = query.indexOf('@EndTime') !== -1 ? `EndTime = ${endTime.toISOString()}` : '' this.logger.info(`Executing "${query}" with ${startTimeLog} ${endTimeLog}`) - this.statusData['Last SQL request'] = `"${query}" with ${startTimeLog} ${endTimeLog}` - this.updateStatusDataStream() + this.updateStatusDataStream({ 'Last SQL request': `"${query}" with ${startTimeLog} ${endTimeLog}` }) } /**