move sparkplug decoding to backend

This commit is contained in:
Thomas Nordquist
2022-02-27 22:49:54 +01:00
parent 72400af679
commit b17b54490e
12 changed files with 3423 additions and 3687 deletions

View File

@@ -28,7 +28,6 @@
"d3-shape": "^1.3.5", "d3-shape": "^1.3.5",
"diff": "^4.0.1", "diff": "^4.0.1",
"dot-prop": "^5.0.0", "dot-prop": "^5.0.0",
"file-loader": "6",
"get-value": "^3.0.1", "get-value": "^3.0.1",
"immutable": "^4.0.0-rc.12", "immutable": "^4.0.0-rc.12",
"in-viewport": "^3.6.0", "in-viewport": "^3.6.0",
@@ -73,6 +72,7 @@
"chai": "^4.2.0", "chai": "^4.2.0",
"cross-env": "^7.0.2", "cross-env": "^7.0.2",
"css-loader": "^3.0.0", "css-loader": "^3.0.0",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.5.0", "html-webpack-plugin": "^5.5.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mocha": "^9.2.1", "mocha": "^9.2.1",
@@ -90,4 +90,4 @@
"peerDependencies": { "peerDependencies": {
"electron": "^17" "electron": "^17"
} }
} }

View File

@@ -1,13 +1,13 @@
import * as q from '../../../../../backend/src/Model' import * as q from '../../../../../backend/src/Model'
import * as React from 'react' import * as React from 'react'
import * as fs from 'fs'
import CodeDiff from '../CodeDiff' import CodeDiff from '../CodeDiff'
import { AppState } from '../../../reducers' import { AppState } from '../../../reducers'
import { Base64Message } from '../../../../../backend/src/Model/Base64Message' import { Base64Message } from '../../../../../backend/src/Model/Base64Message'
import { Payload } from '../../../../../backend/src/Model/sparkplugb' import { SparkplugPayload } from '../../../../../backend/src/Model/SparkplugB'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { ValueRendererDisplayMode } from '../../../reducers/Settings' import { ValueRendererDisplayMode } from '../../../reducers/Settings'
import { Fade } from '@material-ui/core' import { Fade } from '@material-ui/core'
import { Decoder } from '../../../../../backend/src/Model/Decoder'
interface Props { interface Props {
message: q.Message message: q.Message
@@ -49,20 +49,7 @@ class ValueRenderer extends React.Component<Props, State> {
try { try {
JSON.parse(str) JSON.parse(str)
} catch (error) { } catch (error) {
try { return [str, undefined]
//Sparkplugb
if (Payload === undefined) {
throw Error('sparkplugb.Payload is not loaded yet')
}
let json = Payload.toObject(Payload.decode(Base64Message.toUint8Array(msg)), {
longs: String,
enums: String,
bytes: String,
})
return [JSON.stringify(json, undefined, ' '), 'json']
} catch (error) {
return [str, undefined]
}
} }
return [this.messageToPrettyJson(str), 'json'] return [this.messageToPrettyJson(str), 'json']
@@ -98,7 +85,10 @@ class ValueRenderer extends React.Component<Props, State> {
} }
public render() { public render() {
return <div style={{ padding: '0px 0px 8px 0px', width: '100%' }}>{this.renderValue()}</div> return <div style={{ padding: '0px 0px 8px 0px', width: '100%' }}>
{this.props.message?.payload?.decoder === Decoder.SPARKPLUG && "Decoded SparkplugB"}
{this.renderValue()}
</div>
} }
public renderValue() { public renderValue() {

View File

@@ -5,7 +5,6 @@ import PlotHistory from './Chart/Chart'
import { Base64Message } from '../../../backend/src/Model/Base64Message' import { Base64Message } from '../../../backend/src/Model/Base64Message'
import { toPlottableValue } from './Sidebar/CodeDiff/util' import { toPlottableValue } from './Sidebar/CodeDiff/util'
import { PlotCurveTypes } from '../reducers/Charts' import { PlotCurveTypes } from '../reducers/Charts'
import { Payload } from '../../../backend/src/Model/sparkplugb'
const parseDuration = require('parse-duration') const parseDuration = require('parse-duration')
interface Props { interface Props {
@@ -41,7 +40,7 @@ function nodeDotPathToHistory(startTime: number | undefined, history: q.MessageH
let json: any = {} let json: any = {}
try { try {
json = message.payload ? JSON.parse(Base64Message.toUnicodeString(message.payload)) : {} json = message.payload ? JSON.parse(Base64Message.toUnicodeString(message.payload)) : {}
} catch (ignore) {} } catch (ignore) { }
const value = dotProp.get(json, dotPath) const value = dotProp.get(json, dotPath)

View File

@@ -10,11 +10,9 @@ import { connect, Provider } from 'react-redux'
import { ThemeProvider } from '@material-ui/styles' import { ThemeProvider } from '@material-ui/styles'
import './utils/tracking' import './utils/tracking'
import { themes } from './theme' import { themes } from './theme'
import { loadSparkplugBPayload } from '../../backend/src/Model/sparkplugb'
const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
const store = createStore(reducers, composeEnhancers(applyMiddleware(reduxThunk, batchDispatchMiddleware))) const store = createStore(reducers, composeEnhancers(applyMiddleware(reduxThunk, batchDispatchMiddleware)))
loadSparkplugBPayload()
function ApplicationRenderer(props: { theme: 'light' | 'dark' }) { function ApplicationRenderer(props: { theme: 'light' | 'dark' }) {
return ( return (

View File

@@ -2054,13 +2054,13 @@ faye-websocket@^0.11.3:
dependencies: dependencies:
websocket-driver ">=0.5.1" websocket-driver ">=0.5.1"
file-loader@6: file-loader@^6.2.0:
version "6.0.0" version "6.2.0"
resolved "https://registry.npmjs.org/file-loader/-/file-loader-6.0.0.tgz" resolved "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz"
integrity sha512-/aMOAYEFXDdjG0wytpTL5YQLfZnnTmLNjn+AIrJ/6HVnTfDqLsVKUUwkDf4I4kgex36BvjuXEn/TX9B/1ESyqQ== integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==
dependencies: dependencies:
loader-utils "^2.0.0" loader-utils "^2.0.0"
schema-utils "^2.6.5" schema-utils "^3.0.0"
fill-range@^7.0.1: fill-range@^7.0.1:
version "7.0.1" version "7.0.1"
@@ -3766,7 +3766,7 @@ schema-utils@^2.6.5:
ajv "^6.12.0" ajv "^6.12.0"
ajv-keywords "^3.4.1" ajv-keywords "^3.4.1"
schema-utils@^3.1.0, schema-utils@^3.1.1: schema-utils@^3.0.0, schema-utils@^3.1.0, schema-utils@^3.1.1:
version "3.1.1" version "3.1.1"
resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz" resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz"
integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==

View File

@@ -1,15 +1,18 @@
const { Base64 } = require('js-base64')
import { Base64 } from 'js-base64'
import { Decoder } from './Decoder'
export class Base64Message { export class Base64Message {
private base64Message: string private base64Message: string
private unicodeValue: string private unicodeValue: string
public decoder: Decoder
public length: number public length: number
private constructor(base64Str: string) { private constructor(base64Str: string) {
this.base64Message = base64Str this.base64Message = base64Str
this.unicodeValue = Base64.decode(base64Str) this.unicodeValue = Base64.decode(base64Str)
this.length = base64Str.length this.length = base64Str.length
this.decoder = Decoder.NONE
} }
public static toUnicodeString(message: Base64Message) { public static toUnicodeString(message: Base64Message) {
@@ -27,15 +30,4 @@ export class Base64Message {
public static toDataUri(message: Base64Message, mimeType: string) { public static toDataUri(message: Base64Message, mimeType: string) {
return `data:${mimeType};base64,${message.base64Message}` return `data:${mimeType};base64,${message.base64Message}`
} }
public static toUint8Array(message: Base64Message) {
const binaryString = window.atob(message.base64Message)
const len = binaryString.length
const bytes = new Uint8Array(len)
for (const i of Array.from(Array(len).keys())) {
bytes[i] = binaryString.charCodeAt(i)
}
return bytes
}
} }

View File

@@ -0,0 +1,4 @@
export enum Decoder {
NONE,
SPARKPLUG
}

View File

@@ -1,10 +1,24 @@
const protobuf = require('protobufjs') import { readFileSync } from 'fs'
const sparkplugBProto = require('../../../res/sparkplug_b.proto') import * as protobuf from 'protobufjs'
import { Base64Message } from './Base64Message';
import { Decoder } from './Decoder';
export let Payload = undefined const buffer = readFileSync(require.resolve('../../../../res/sparkplug_b.proto'));
const root = protobuf.parse(buffer.toString()).root
export let SparkplugPayload = root.lookupType('com.cirruslink.sparkplug.protobuf.Payload')
export function loadSparkplugBPayload() { export const SparkplugDecoder = {
protobuf.load(sparkplugBProto).then((root: any) => { decode(input: Buffer): Base64Message | undefined {
Payload = root.lookupType('com.cirruslink.sparkplug.protobuf.Payload') try {
}) let message = Base64Message.fromString(
JSON.stringify(
SparkplugPayload.toObject(SparkplugPayload.decode(new Uint8Array(input)))
)
)
message.decoder = Decoder.SPARKPLUG
return message
} catch {
// ignore
}
}
} }

View File

@@ -2,11 +2,42 @@ import { TreeNodeFactory } from '../'
import { Base64Message } from '../Base64Message' import { Base64Message } from '../Base64Message'
import { TreeNode } from '../TreeNode' import { TreeNode } from '../TreeNode'
import { MqttMessage } from '../../../../events' import { MqttMessage } from '../../../../events'
import { SparkplugPayload } from '../sparkplugb'
interface Decoder {
decode(input: string): string | null
}
const SparkplugDecoder = {
decoderTime: 0,
encoder: new TextEncoder(),
decode(input: string): string | null {
if (!SparkplugPayload) {
return null
}
const start = performance.now()
let result
try {
result = JSON.stringify(SparkplugPayload.toObject(SparkplugPayload.decode(this.encoder.encode(input))))
} catch { }
this.decoderTime += performance.now() - start;
return result ?? null
}
}
let i = 1
setInterval(() => {
console.log(`decoder time after ${i++ * 10} seconds: ${SparkplugDecoder.decoderTime}ms`)
}, 10000)
export function makeTreeNode(topic: string, message?: string): TreeNode<any> { export function makeTreeNode(topic: string, message?: string): TreeNode<any> {
let sparkplugMessage = message && SparkplugDecoder.decode(message)
const mqttMessage: MqttMessage = { const mqttMessage: MqttMessage = {
topic, topic,
payload: message ? Base64Message.fromString(message) : null, payload: message ? Base64Message.fromString(sparkplugMessage ?? message) : null,
qos: 0, qos: 0,
retain: false, retain: false,
messageId: undefined, messageId: undefined,

View File

@@ -10,6 +10,7 @@ import {
makePublishEvent, makePublishEvent,
removeConnection, removeConnection,
} from '../../events' } from '../../events'
import { SparkplugDecoder } from './Model/sparkplugb'
export class ConnectionManager { export class ConnectionManager {
private connections: { [s: string]: DataSource<any> } = {} private connections: { [s: string]: DataSource<any> } = {}
@@ -48,7 +49,7 @@ export class ConnectionManager {
backendEvents.emit(messageEvent, { backendEvents.emit(messageEvent, {
topic, topic,
payload: Base64Message.fromBuffer(buffer), payload: SparkplugDecoder.decode(buffer) ?? Base64Message.fromBuffer(buffer),
qos: packet.qos, qos: packet.qos,
retain: packet.retain, retain: packet.retain,
messageId: packet.messageId, messageId: packet.messageId,

View File

@@ -108,7 +108,7 @@
"electron-log": "4.4.6", "electron-log": "4.4.6",
"electron-updater": "^4.6", "electron-updater": "^4.6",
"fs-extra": "9", "fs-extra": "9",
"js-base64": "^2.5.1", "js-base64": "^3.7.2",
"json-to-ast": "^2.1.0", "json-to-ast": "^2.1.0",
"lowdb": "^1.0.0", "lowdb": "^1.0.0",
"mime": "^2.4.4", "mime": "^2.4.4",
@@ -118,4 +118,4 @@
"uuid": "^8.3.2", "uuid": "^8.3.2",
"yarn-run-all": "^3.1.1" "yarn-run-all": "^3.1.1"
} }
} }

6975
yarn.lock

File diff suppressed because it is too large Load Diff