diff --git a/src/spec/demoVideo.ts b/src/spec/demoVideo.ts index ef76c98..bec1b97 100644 --- a/src/spec/demoVideo.ts +++ b/src/spec/demoVideo.ts @@ -5,6 +5,7 @@ import * as path from 'path' import { ElectronApplication, _electron as electron } from 'playwright' import mockMqtt, { stop as stopMqtt } from './mock-mqtt' +import { default as MockSparkplug } from './mock-sparkplugb' import { clearOldTopics } from './scenarios/clearOldTopics' import { clearSearch, searchTree } from './scenarios/searchTree' import { clickOnHistory, createFakeMousePointer, hideText, showText, sleep } from './util' @@ -64,6 +65,7 @@ async function doStuff() { const scenes = new SceneBuilder() await scenes.record('connect', async () => { await connectTo('127.0.0.1', page) + await MockSparkplug.run() // Start sparkplug client after connect or birth topics will be missed await sleep(1000) }) @@ -142,7 +144,7 @@ async function doStuff() { }) stopMqtt() - console.log('Stopped mqtt') + console.log('Stopped mqtt client') cleanUp(scenes, electronApp) } diff --git a/src/spec/mock-sparkplugb.ts b/src/spec/mock-sparkplugb.ts new file mode 100644 index 0000000..127b218 --- /dev/null +++ b/src/spec/mock-sparkplugb.ts @@ -0,0 +1,249 @@ +/******************************************************************************** + * Copyright (c) 2016-2018 Cirrus Link Solutions and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Cirrus Link Solutions - initial implementation + ********************************************************************************/ +import * as SparkplugClient from 'sparkplug-client' +import type { UPayload } from 'sparkplug-client' +import type { UMetric } from 'sparkplug-payload/lib/sparkplugbpayload' + +/* + * Main sample function which includes the run() function for running the sample + */ + + +export interface MockSparkplugClient { + stop: () => void +} + +var sample = (function () { + var config = { + serverUrl: 'tcp://127.0.0.1:1883', + username: '', + password: '', + groupId: 'Sparkplug Devices', + edgeNode: 'JavaScript Edge Node', + clientId: 'JavaScriptSimpleEdgeNode', + version: 'spBv1.0', + }, + hwVersion = 'Emulated Hardware', + swVersion = 'v1.0.0', + deviceId = 'Emulated Device', + sparkPlugClient, + publishPeriod = 5000, + // Generates a random integer + randomInt = function () { + return 1 + Math.floor(Math.random() * 10) + }, + // Get BIRTH payload for the edge node + getNodeBirthPayload = function (): UPayload { + return { + timestamp: new Date().getTime(), + metrics: [ + { + name: 'Node Control/Rebirth', + type: 'Boolean', + value: false, + }, + { + name: 'Template1', + type: 'Template', + value: { + isDefinition: true, + metrics: [ + { name: 'myBool', value: false, type: 'Boolean' }, + { name: 'myInt', value: 0, type: 'UInt32' }, + ], + parameters: [ + { + name: 'param1', + type: 'String', + value: 'value1', + }, + ], + }, + }, + ], + } + }, + // Get BIRTH payload for the device + getDeviceBirthPayload = function (): UPayload { + return { + timestamp: new Date().getTime(), + metrics: [ + { name: 'my_boolean', value: Math.random() > 0.5, type: 'Boolean' }, + { name: 'my_double', value: Math.random() * 0.123456789, type: 'Double' }, + { name: 'my_float', value: Math.random() * 0.123, type: 'Float' }, + { name: 'my_int', value: randomInt(), type: 'Int8' }, + { name: 'my_long', value: randomInt() * 214748364700, type: 'Int64' }, + { name: 'Inputs/0', value: true, type: 'Boolean' }, + { name: 'Inputs/1', value: 0, type: 'Int8' }, + { name: 'Inputs/2', value: 1.23, type: 'UInt64' }, + { name: 'Outputs/0', value: true, type: 'Boolean' }, + { name: 'Outputs/1', value: 0, type: 'Int16' }, + { name: 'Outputs/2', value: 1.23, type: 'UInt64' }, + { name: 'Properties/hw_version', value: hwVersion, type: 'String' }, + { name: 'Properties/sw_version', value: swVersion, type: 'String' }, + { + name: 'my_dataset', + type: 'DataSet', + value: { + numOfColumns: 2, + types: ['String', 'String'], + columns: ['str1', 'str2'], + rows: [ + ['x', 'a'], + ['y', 'b'], + ], + }, + }, + { + name: 'TemplateInstance1', + type: 'Template', + value: { + templateRef: 'Template1', + isDefinition: false, + metrics: [ + { name: 'myBool', value: true, type: 'Boolean' }, + { name: 'myInt', value: 100, type: 'Int8' }, + ], + parameters: [ + { + name: 'param1', + type: 'String', + value: 'value2', + }, + ], + }, + }, + ], + } + }, + // Get data payload for the device + getDataPayload = function (): UPayload { + return { + timestamp: new Date().getTime(), + metrics: [ + { name: 'my_boolean', value: Math.random() > 0.5, type: 'Boolean' }, + { name: 'my_double', value: Math.random() * 0.123456789, type: 'Double' }, + { name: 'my_float', value: Math.random() * 0.123, type: 'UInt64' }, + { name: 'my_int', value: randomInt(), type: 'Int16' }, + { name: 'my_long', value: randomInt() * 214748364700, type: 'UInt64' }, + ], + } + }, + // Runs the sample + run = async function (): Promise { + // Create the SparkplugClient + const sparkplugClient = SparkplugClient.newClient(config) + let updateInterval: NodeJS.Timeout | null = null + const connected = new Promise((resolve) => { + + // Create 'birth' handler + sparkplugClient.on('birth', () => { + // Publish Node BIRTH certificate + sparkplugClient.publishNodeBirth(getNodeBirthPayload()) + // Publish Device BIRTH certificate + sparkplugClient.publishDeviceBirth(deviceId, getDeviceBirthPayload()) + resolve({ + stop: () => { + if (updateInterval) { + clearInterval(updateInterval) + } + sparkplugClient.stop() + } + }) + }) + }) + + // Create Incoming Message Handler + sparkplugClient.on('message', function (topic: string, payload: UPayload) { + console.log(topic, payload) + }) + + // Create node command handler + // spell-checker: disable-next-line + sparkplugClient.on('ncmd', function (payload: UPayload) { + var timestamp = payload.timestamp, + metrics = payload.metrics + + if (metrics !== undefined && metrics !== null) { + for (var i = 0; i < metrics.length; i++) { + var metric = metrics[i] + if (metric.name == 'Node Control/Rebirth' && metric.value) { + console.log("Received 'Rebirth' command") + // Publish Node BIRTH certificate + sparkplugClient.publishNodeBirth(getNodeBirthPayload()) + // Publish Device BIRTH certificate + sparkplugClient.publishDeviceBirth(deviceId, getDeviceBirthPayload()) + } + } + } + }) + + // Create device command handler + // spell-checker: disable-next-line + sparkplugClient.on('dcmd', function (deviceId: string, payload: UPayload) { + var timestamp = payload.timestamp, + metrics = payload.metrics, + inboundMetricMap: { [name: string]: any } = {}, + outboundMetric: Array = [], + outboundPayload: UPayload + + console.log('Command received for device ' + deviceId) + + // Loop over the metrics and store them in a map + if (metrics !== undefined && metrics !== null) { + for (var i = 0; i < metrics.length; i++) { + var metric = metrics[i] + if (metric.name !== undefined && metric.name !== null) { + inboundMetricMap[metric.name] = metric.value + } + } + } + if (inboundMetricMap['Outputs/0'] !== undefined && inboundMetricMap['Outputs/0'] !== null) { + console.log('Outputs/0: ' + inboundMetricMap['Outputs/0']) + outboundMetric.push({ name: 'Inputs/0', value: inboundMetricMap['Outputs/0'], type: 'Boolean' }) + outboundMetric.push({ name: 'Outputs/0', value: inboundMetricMap['Outputs/0'], type: 'Boolean' }) + console.log('Updated value for Inputs/0 ' + inboundMetricMap['Outputs/0']) + } else if (inboundMetricMap['Outputs/1'] !== undefined && inboundMetricMap['Outputs/1'] !== null) { + console.log('Outputs/1: ' + inboundMetricMap['Outputs/1']) + outboundMetric.push({ name: 'Inputs/1', value: inboundMetricMap['Outputs/1'], type: 'Int32' }) + outboundMetric.push({ name: 'Outputs/1', value: inboundMetricMap['Outputs/1'], type: 'Int32' }) + console.log('Updated value for Inputs/1 ' + inboundMetricMap['Outputs/1']) + } else if (inboundMetricMap['Outputs/2'] !== undefined && inboundMetricMap['Outputs/2'] !== null) { + console.log('Outputs/2: ' + inboundMetricMap['Outputs/2']) + outboundMetric.push({ name: 'Inputs/2', value: inboundMetricMap['Outputs/2'], type: 'UInt64' }) + outboundMetric.push({ name: 'Outputs/2', value: inboundMetricMap['Outputs/2'], type: 'UInt64' }) + console.log('Updated value for Inputs/2 ' + inboundMetricMap['Outputs/2']) + } + + outboundPayload = { + timestamp: new Date().getTime(), + metrics: outboundMetric, + } + + // Publish device data + sparkplugClient.publishDeviceData(deviceId, outboundPayload) + }) + + updateInterval = setInterval(function () { + // Publish device data + sparkplugClient.publishDeviceData(deviceId, getDataPayload()) + + + }, 2000) + return connected + } + + return { run: run } +})() + +export default sample \ No newline at end of file